select userinfo from 知乎

上篇文章说目标是想拉一丢知乎的用户数据 这周正好有时间 给他整一发大的

BiuBiuBiu
  • 目标

1 通过登录账号获取关注用户(第一层)
2 通过获取到的关注用 继续循环获取关注用户 level++
3 用户信息表格截个图 大概就是这些


用户数据详情表
  • 实现方式

1 语言 Python3
2 库--requests gevent BeautifulSoup(lxml)
3 数据库 MongoDB
4 PIL(图像库) pytesseract(验证码识别-识别率低(不是重点))

  • 整体过程实现逻辑

1 登陆账号 以当前账号为中心用户 获取基本信息 保存数据库 默认完成状态是false
2 循环开启 每次读取最早的一条 status=False的用户 异步 读取关联url数据存储 依次循环


逻辑实现
  • 现在一步一步做

整个代码结构图

代码结构

本来还想开着多进程 后面试了一下 没几下知乎就给我限制ip了
发现当前这种 算是没有在没有代理ip的情况下速度还算不错的
后面有爬取一个代理ip网站 但是用起来多数不可用或者说可用时间太短(正式因为被限制ip了 和 mac总是黑屏后 自动断网 就算了 目的达到了 也抓了一丢 ~

总数据量

还抓到两个同名的--还都是技术的

-###代码过程
1 登陆 要从当前用户开始 先得登陆 保持登陆状态 就是保持cookie状态 cookie作为服务器识别你是否登陆的标志
所以说到底我们就要在登陆这个操作我们模拟完成后 存cookie 后续的链接访问后带上这个cookie 这里我们用的requests 特别方便 直接session-requests就能共享上个连接的cookie
登陆中 有可能遇到验证码 这里用到了自动识别三次 失败就会弹出图片手动输入

  • getMyCenterURL 判断登陆成功与否 随便用登陆成功后能娶到的一个标识来判断就行
    '''
    if name == 'main':
    account = 'phone or email'
    secret = 'XXOO'
    refer="http://www.zhihu.com/"
    if(getMyCenterURL()):
    #登陆状态
    pass
    else:
    LoginActon.login(secret,account,session,headers)
    getMyCenterURL()

    '''
    
  • 登陆没成功说明cookie失效 这里通过抓取到的接口来登陆 至于怎么抓取 网上很多讲解的 主要步骤
    1 chrome F12 前往知乎登陆页面 故意输错密码 抓取登陆接口 与 request from表单 看post的内容 这个网上很多
    2 验证码 同意的抓取 这里用了自动是别的一个库 不过成功率 太低 三次自动识别失败 就弹出图片手动输入
    3 cookie保存
    cookie载入 名为cookies
    '''
    session = requests.session()
    session.cookies = cookielib.LWPCookieJar(filename='cookies')
    try:
    session.cookies.load(ignore_discard=True)
    except:
    print("Cookie 未能加载")
    '''
    登陆之后 session.cookies.save()保存
    4 登陆成功后 就可以开始抓取数据了 先把自己作为第一个用户抓取保存数据库 后面就以自己为中心开始抓取


    login ok 后的数据抓取调用
  • 数据抓取 DataParseAction类 看看整体代码
    两个方法
    1 根据用户关注用户url获取关注列表用户
    2 根据用户主页 抓取用户基本新保存数据库
    方法一 在while 循环中一条一条的从数据库取出来 塞入方法2中 方法2保存具体数据

    具体代码
    import random
    import time
    import gevent
    import pymongo
    import requests
    
    __author__ = 'Daemon1993'
    
    from bs4 import BeautifulSoup, SoupStrainer
    from ZHSpider import LoginActon
    from ZHSpider import getProxyIP
    
    '''
    解析 用户主页数据
    parmas url userHomePage
    get all attention users
    '''
    
    # 全局 count 每次解析一个 就加一
    count = 0
    sleep_time = 0
    sleep_timeCount = 0
    
    # tagName_ClassName 获取相关数据
    def getTagTextByName_Class(soup, tagName, class_name, data, key):
        try:
            value = soup.find(tagName, class_=class_name).text
            data[key] = value
        except Exception as e:
            pass
    
    # getTitle by Name_Class
    def getTagTitleByName_Class(soup, tagName, class_name, data, key):
        try:
            value = soup.find(tagName, class_=class_name).get('title')
            data[key] = value
        except:
            pass
    
    def getSexByName_Class(soup, tagName, class_name, data, key):
        try:
            data[key] = "未知"
            value = soup.find(tagName, class_=class_name)
            value = value.find('i')
            tags = value.get('class')
            tag_str = "".join(tags)
    
            if (tag_str.find('female') != -1):
                data[key] = "female"
            else:
                data[key] = "male"
        except:
            pass
    
   # 获取关注详情
    def getFollowsDetail(soup, tag1, class1, tag2, class2, data, attr_name, key):
        try:
            data[key] = LoginActon.index_url + soup.find(tag1, class_=class1).find(tag2, class_=class2).get(attr_name)
            # 获取 关注信息
            index = 0
            for tag in soup.find(tag1, class_=class1).find_all("strong"):
                if (index == 0):
                    data["followees"] = tag.text
                else:
                    data["followers"] = tag.text
                index += 1
        except:
            return False
            pass
    
    def getAttentionContent(soup, data):
        try:
            topics = []
            for img in soup.find("div", class_="zm-profile-side-topics").find_all("img"):
                topics.append(img.get('alt'))
            data["topics"] = topics
        except:
            pass
    
    def changeRefer(headers,refer):
        headers["Referer"]=refer
    
    # 根据URL获取 数据 解析保存
    def saveDataByUrl(from_url, url, headers, zh, relation_level):
        data = {}
    
        data['_id'] = url
        data['from_url'] = from_url
    
        global sleep_time
        if(sleep_time!=0):
            sleep_time=random.randint(0,5)
            if(sleep_time>3):
                print('sleep {0}'.format(sleep_time))
            time.sleep(sleep_time)
    
        r = requests.session()
    
        try:
            if(from_url!=url):
                changeRefer(headers,from_url)
    
            html = r.get(url,timeout=5.0, headers=headers)
        except Exception as e:
            print("saveDataByUrl {0}".format(e))
            return
            pass
        only_data_info = SoupStrainer("div", class_="zm-profile-header-main")
    
        soup_info = BeautifulSoup(html.text, "lxml", parse_only=only_data_info)
    
        getTagTextByName_Class(soup_info, "span", "name", data, "name")
        getTagTitleByName_Class(soup_info, "div", "bio ellipsis", data, "introduction")
        getTagTitleByName_Class(soup_info, "span", "location item", data, "location")
        getTagTitleByName_Class(soup_info, "span", "business item", data, "business")
    
        getSexByName_Class(soup_info, "span", "item gender", data, "gender")
    
        getTagTitleByName_Class(soup_info, "span", "employment item", data, "work_adr")
        getTagTitleByName_Class(soup_info, "span", "position item", data, "work_direction")
        getTagTitleByName_Class(soup_info, "span", "education item", data, "education_school")
        getTagTitleByName_Class(soup_info, "span", "education-extra item", data, "education_direction")
    
        try:
            description = soup_info.select('span[class="description unfold-item"] span[class="content"]')[0].get_text()
            data["description"] = description
        except Exception as e:
            pass
    
        # 关注行为
        only_data_action = SoupStrainer("div", class_="zu-main-sidebar")
        soup_action = BeautifulSoup(html.text, "lxml", parse_only=only_data_action)
    
        getFollowsDetail(soup_action,
                                      "div", "zm-profile-side-following zg-clear",
                                      "a", "item",
                                      data, "href", "followees_url")
    
        # 获取关注话题
        getAttentionContent(soup_action, data)
        global count
    
        try:
            data["relation_level"] = relation_level
            # 当前账号 的关注账号 默认没有被全部加载
            data["followees_status"] = False
    
            if(count%50==0):
                print(data)
            #200一次随机大于 不停 小于停
            if(count%200==0):
                if(sleep_time>3):
                    sleep_time=0
                else:
                    sleep_time=random.randint(0,5)
    
            zh.insert(data)
        except:
            pass
    
        count += 1
        return True
    
      def startSpider(session, headers, zh):
        print('知乎爬虫 开始工作 ------ 飞起来。。。。')
    
        # 获取当前DBzhong status=False的所有URL 最大5000
        while True:
            tasks = []
            userinfo= zh.find_one({"followees_status": False})
            if(userinfo is None):
                break
            from_url = userinfo['_id']
            try:
                followees_url = userinfo['followees_url']
            except:
                zh.remove(from_url)
                print('delete {0} '.format(from_url))
                continue
                pass
            relation_level = userinfo['relation_level']
    
            tasks.append(gevent.spawn(getAllAtentionUsers,
                                      from_url, followees_url, session, headers, zh, relation_level + 1,userinfo))
    
            gevent.joinall(tasks)
    
         # 获取当前所有的关注用户列表 返回
    def getAllAtentionUsers(from_url, follows_url, session, headers, zh, relation_level,userinfo):
        if (follows_url is None):
            return
        html = ""
        r = requests.session()
        r.cookies = session.cookies
    
        try:
            changeRefer(headers,from_url)
    
            html = r.get(follows_url, timeout=5.0, headers=headers).text
        except Exception as e:
            pass
    
        relation_info = SoupStrainer("div", class_="zm-profile-section-wrap zm-profile-followee-page")
        soup = BeautifulSoup(html, "lxml", parse_only=relation_info)
    
        urls = []
        for user in soup.find_all("div", class_="zm-profile-card zm-profile-section-item zg-clear no-hovercard"):
            user_a = user.find("a")
            url = LoginActon.index_url + user_a.get('href')
            urls.append(url)
    
       # 保存每个关注的用户信息
        print('user {0} follows size{1} '.format(from_url, len(urls)))
    
        tasks = [gevent.spawn(saveDataByUrl, from_url, url, headers, zh, relation_level) for url in urls]
        gevent.joinall(tasks)
    
        try:
            userinfo["followees_status"]=True
            zh.save(userinfo)
        except:
            pass
    
        print('用户 {0} followees save OK  save count {1}'.format(from_url, count))
  • 遇到状况

周六晚上代码写完后 挂着跑了一晚上 不知道为啥电脑熄屏后 网络断了 获取了7000多条 relation_level 到了4
还特意设置永不睡眠 周天白天重新开始 💥
然后知乎给我限制ip啦
也不知道为啥 电脑能访问 请求返回 提示 ip次数过多 然后今天一天都在研究怎么绕开归根打的就是 不要只用一个ip 你可以多台机器爬去 可以每次拨号动态分配ip 什么的
后面我也去扒了一些免费代理ip网站的ip

有点可怕

用起来也不是顺利 大部分不能用 明明验证百度能过 访问知乎就readTimeout 这里整了几个小时后 后面 用一个list存ip
从本机开始 如果失败 就取新的ip删除list中旧的 但是一运行 大部分超时 能用的也很慢 (不太理想)后面还是放弃了 周一上班去 电脑挂着 回来又断网了 想想先这样吧


这些代理ip
  • 项目要跑起来
    1 用户名密码输入
    2 有mongoDB数据库 本地安装也行
    github地址
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,384评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,845评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,148评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,640评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,731评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,712评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,703评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,473评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,915评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,227评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,384评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,063评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,706评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,302评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,531评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,321评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,248评论 2 352

推荐阅读更多精彩内容