美文网首页
select userinfo from 知乎

select userinfo from 知乎

作者: Daemon1993 | 来源:发表于2016-09-05 23:25 被阅读145次

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

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地址

相关文章

网友评论

      本文标题:select userinfo from 知乎

      本文链接:https://www.haomeiwen.com/subject/qizlettx.html