select userinfo from 知乎

上篇文章說目標是想拉一丟知乎的用戶數(shù)據(jù) 這周正好有時間 給他整一發(fā)大的

BiuBiuBiu
  • 目標

1 通過登錄賬號獲取關注用戶(第一層)
2 通過獲取到的關注用 繼續(xù)循環(huán)獲取關注用戶 level++
3 用戶信息表格截個圖 大概就是這些


用戶數(shù)據(jù)詳情表
  • 實現(xiàn)方式

1 語言 Python3
2 庫--requests gevent BeautifulSoup(lxml)
3 數(shù)據(jù)庫 MongoDB
4 PIL(圖像庫) pytesseract(驗證碼識別-識別率低(不是重點))

  • 整體過程實現(xiàn)邏輯

1 登陸賬號 以當前賬號為中心用戶 獲取基本信息 保存數(shù)據(jù)庫 默認完成狀態(tài)是false
2 循環(huán)開啟 每次讀取最早的一條 status=False的用戶 異步 讀取關聯(lián)url數(shù)據(jù)存儲 依次循環(huán)


邏輯實現(xiàn)
  • 現(xiàn)在一步一步做

整個代碼結構圖

代碼結構

本來還想開著多進程 后面試了一下 沒幾下知乎就給我限制ip了
發(fā)現(xiàn)當前這種 算是沒有在沒有代理ip的情況下速度還算不錯的
后面有爬取一個代理ip網(wǎng)站 但是用起來多數(shù)不可用或者說可用時間太短(正式因為被限制ip了 和 mac總是黑屏后 自動斷網(wǎng) 就算了 目的達到了 也抓了一丟 ~

總數(shù)據(jù)量

還抓到兩個同名的--還都是技術的

-###代碼過程
1 登陸 要從當前用戶開始 先得登陸 保持登陸狀態(tài) 就是保持cookie狀態(tài) cookie作為服務器識別你是否登陸的標志
所以說到底我們就要在登陸這個操作我們模擬完成后 存cookie 后續(xù)的鏈接訪問后帶上這個cookie 這里我們用的requests 特別方便 直接session-requests就能共享上個連接的cookie
登陸中 有可能遇到驗證碼 這里用到了自動識別三次 失敗就會彈出圖片手動輸入

  • getMyCenterURL 判斷登陸成功與否 隨便用登陸成功后能娶到的一個標識來判斷就行
    '''
    if name == 'main':
    account = 'phone or email'
    secret = 'XXOO'
    refer="http://www.zhihu.com/"
    if(getMyCenterURL()):
    #登陸狀態(tài)
    pass
    else:
    LoginActon.login(secret,account,session,headers)
    getMyCenterURL()

    '''
    
  • 登陸沒成功說明cookie失效 這里通過抓取到的接口來登陸 至于怎么抓取 網(wǎng)上很多講解的 主要步驟
    1 chrome F12 前往知乎登陸頁面 故意輸錯密碼 抓取登陸接口 與 request from表單 看post的內容 這個網(wǎng)上很多
    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 登陸成功后 就可以開始抓取數(shù)據(jù)了 先把自己作為第一個用戶抓取保存數(shù)據(jù)庫 后面就以自己為中心開始抓取


    login ok 后的數(shù)據(jù)抓取調用
  • 數(shù)據(jù)抓取 DataParseAction類 看看整體代碼
    兩個方法
    1 根據(jù)用戶關注用戶url獲取關注列表用戶
    2 根據(jù)用戶主頁 抓取用戶基本新保存數(shù)據(jù)庫
    方法一 在while 循環(huán)中一條一條的從數(shù)據(jù)庫取出來 塞入方法2中 方法2保存具體數(shù)據(jù)

    具體代碼
    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
    
    '''
    解析 用戶主頁數(shù)據(jù)
    parmas url userHomePage
    get all attention users
    '''
    
    # 全局 count 每次解析一個 就加一
    count = 0
    sleep_time = 0
    sleep_timeCount = 0
    
    # tagName_ClassName 獲取相關數(shù)據(jù)
    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
    
    # 根據(jù)URL獲取 數(shù)據(jù) 解析保存
    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))
  • 遇到狀況

周六晚上代碼寫完后 掛著跑了一晚上 不知道為啥電腦熄屏后 網(wǎng)絡斷了 獲取了7000多條 relation_level 到了4
還特意設置永不睡眠 周天白天重新開始 ??
然后知乎給我限制ip啦
也不知道為啥 電腦能訪問 請求返回 提示 ip次數(shù)過多 然后今天一天都在研究怎么繞開歸根打的就是 不要只用一個ip 你可以多臺機器爬去 可以每次撥號動態(tài)分配ip 什么的
后面我也去扒了一些免費代理ip網(wǎng)站的ip

有點可怕

用起來也不是順利 大部分不能用 明明驗證百度能過 訪問知乎就readTimeout 這里整了幾個小時后 后面 用一個list存ip
從本機開始 如果失敗 就取新的ip刪除list中舊的 但是一運行 大部分超時 能用的也很慢 (不太理想)后面還是放棄了 周一上班去 電腦掛著 回來又斷網(wǎng)了 想想先這樣吧


這些代理ip
  • 項目要跑起來
    1 用戶名密碼輸入
    2 有mongoDB數(shù)據(jù)庫 本地安裝也行
    github地址
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末慰毅,一起剝皮案震驚了整個濱河市撰茎,隨后出現(xiàn)的幾起案子畅哑,更是在濱河造成了極大的恐慌旭绒,老刑警劉巖仇轻,帶你破解...
    沈念sama閱讀 218,284評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件霹期,死亡現(xiàn)場離奇詭異,居然都是意外死亡拯田,警方通過查閱死者的電腦和手機历造,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,115評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來船庇,“玉大人吭产,你說我怎么就攤上這事⊙悸郑” “怎么了臣淤?”我有些...
    開封第一講書人閱讀 164,614評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長窃爷。 經(jīng)常有香客問我邑蒋,道長,這世上最難降的妖魔是什么按厘? 我笑而不...
    開封第一講書人閱讀 58,671評論 1 293
  • 正文 為了忘掉前任医吊,我火速辦了婚禮,結果婚禮上逮京,老公的妹妹穿的比我還像新娘卿堂。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 67,699評論 6 392
  • 文/花漫 我一把揭開白布草描。 她就那樣靜靜地躺著览绿,像睡著了一般。 火紅的嫁衣襯著肌膚如雪穗慕。 梳的紋絲不亂的頭發(fā)上饿敲,一...
    開封第一講書人閱讀 51,562評論 1 305
  • 那天,我揣著相機與錄音逛绵,去河邊找鬼诀蓉。 笑死,一個胖子當著我的面吹牛暑脆,可吹牛的內容都是我干的渠啤。 我是一名探鬼主播,決...
    沈念sama閱讀 40,309評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼添吗,長吁一口氣:“原來是場噩夢啊……” “哼沥曹!你這毒婦竟也來了?” 一聲冷哼從身側響起碟联,我...
    開封第一講書人閱讀 39,223評論 0 276
  • 序言:老撾萬榮一對情侶失蹤妓美,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后鲤孵,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體壶栋,經(jīng)...
    沈念sama閱讀 45,668評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,859評論 3 336
  • 正文 我和宋清朗相戀三年普监,在試婚紗的時候發(fā)現(xiàn)自己被綠了贵试。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,981評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡凯正,死狀恐怖毙玻,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情廊散,我是刑警寧澤桑滩,帶...
    沈念sama閱讀 35,705評論 5 347
  • 正文 年R本政府宣布,位于F島的核電站允睹,受9級特大地震影響运准,放射性物質發(fā)生泄漏。R本人自食惡果不足惜缭受,卻給世界環(huán)境...
    茶點故事閱讀 41,310評論 3 330
  • 文/蒙蒙 一胁澳、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧贯涎,春花似錦听哭、人聲如沸慢洋。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,904評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至败明,卻和暖如春隘马,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背妻顶。 一陣腳步聲響...
    開封第一講書人閱讀 33,023評論 1 270
  • 我被黑心中介騙來泰國打工酸员, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人讳嘱。 一個月前我還...
    沈念sama閱讀 48,146評論 3 370
  • 正文 我出身青樓幔嗦,卻偏偏與公主長得像,于是被迫代替她去往敵國和親沥潭。 傳聞我的和親對象是個殘疾皇子邀泉,可洞房花燭夜當晚...
    茶點故事閱讀 44,933評論 2 355

推薦閱讀更多精彩內容