閱讀943 返回首頁    go 技術社區[雲棲]


python3+urllib打造新浪微博爬蟲,真的很easy

前言(一些廢話)

最近試了一下網頁版的(weibo.com)和移動端的(m.weibo.cn),網頁版的解析一大堆網頁實在是太麻煩,移動端隻需要請求,直接返回json數據,這裏對這兩種方式做了下優缺點對比

網頁版

  • 優點
  1. 用戶信息獲取的很全麵,出生日期、性取向、感情狀況是移動端不能獲取的
  2. 微博、粉絲可以抓取的比較全麵
  3. 可以學習xpath、bs4、正則的網頁解析
  • 缺點
  1. 要花錢哦,因為多賬號登錄需要驗證碼,驗證碼識別的話,用雲打碼(沒用過)是收費的
  2. 解析頁麵惡心的一批,要寫正則、要去看網頁...比較麻煩

移動版

  • 優點
  • 不需要驗證碼
  • 直接解析json
  • 缺點
  • 個人信息不全
  • 微博、粉絲分頁到了250之後,就沒有數據返回了,暫時還沒解決這個是為什麼。

現在用的是移動版,配了5個微博賬號,5個線程在跑,電腦扔在寢室跑,不敢說一天多少多少數據,但現在保持在一天用戶300W、微博200W左右的數據量...隻跑了兩天...

源碼的話,看後麵....

正題

其實思路很簡單,就是通過urllib模擬請求登錄、發請求,然後解析json,存數據庫...當然程序還有很多優化的地方,以後慢慢改進

環境

  • python3
  • django 1.11.3
  • mysql 5.7.17
  • fiddler 4

抓取

寫爬蟲的套路就是,訪問頁麵,分析頁麵行為,也就是頁麵的每一個操作都發了什麼樣的請求,返回了什麼數據,記住這個套路,還有什麼爬蟲不能寫。

模擬登陸

打開m.weibo.cn,打開fiddler 4,開啟https請求的捕捉,不知道怎麼開自行google
當你輸入好賬號、密碼點擊登錄的時候,看fiddler4捕獲的請求


post請求,當然是看看他發了什麼數據啦....

再看看response的數據


返回了用戶登錄的狀態,uid...
你可以訪問 www://weibo.com/u/ + 上圖的uid加一波微博關注(真無恥,強行吸粉)

密碼沒有做任何加密處理,嘻嘻,很簡單吧,現在隻要模擬個post請求就行了。是不是很簡單?

def login(user_name, password, opener):
    LOGGER.info(user_name + ' login')
    args = {
        'username': user_name,
        'password': password,
        'savestate': 1,
        'ec': 0,
        'pagerefer': 'https://passport.weibo.cn/signin/'
                     'welcome?entry=mweibo&r=http%3A%2F%2Fm.weibo.cn%2F&wm=3349&vt=4',
        'entry': 'mweibo',
        'wentry': '',
        'loginfrom': '',
        'client_id': '',
        'code': '',
        'qq': '',
        'hff': '',
        'hfp': ''
    }

    post_data = urllib.parse.urlencode(args).encode()
    try_time = 0
    while try_time < constants.TRY_TIME:
        try:
            resp = opener.open(constants.LOGIN_URL, post_data)
            resp_json = json.loads(resp.read().decode())
            if 'retcode' in resp_json and resp_json['retcode'] == '20000000':
                LOGGER.info("%s login successful" % user_name)
                break
            else:
                LOGGER.warn('login fail:%s' % str(resp_json))
                sleep(10)
                try_time += 1
        except :
            LOGGER.error("login failed")
            LOGGER.error(traceback.print_exc())
            sleep(10)
            try_time += 1
            LOGGER.info('try %d time' % try_time)

看看寫的模擬登錄能不能用,當然要測試啦,這個測試當然是你自己寫啦,反正我已經測試過了,如果不出意外的話,你的測試會不通過,如下URLError

Traceback (most recent call last):
  File "E:\codingspace\python\Jpider\spiders\weibo\weibo_http.py", line 40, in login
    resp = opener.open(constants.LOGIN_URL, post_data)
  File "D:\ProgramData\Anaconda3\lib\urllib\request.py", line 526, in open
    response = self._open(req, data)
  File "D:\ProgramData\Anaconda3\lib\urllib\request.py", line 544, in _open
    '_open', req)
  File "D:\ProgramData\Anaconda3\lib\urllib\request.py", line 504, in _call_chain
    result = func(*args)
  File "D:\ProgramData\Anaconda3\lib\urllib\request.py", line 1361, in https_open
    context=self._context, check_hostname=self._check_hostname)
  File "D:\ProgramData\Anaconda3\lib\urllib\request.py", line 1320, in do_open
    raise URLError(err)
urllib.error.URLError: <urlopen error [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:749)>

哎呀,好煩,又報錯,怎麼辦呢,stackoverflow歡迎你,傳送門https://stackoverflow.com/,有錯自己查,stackoverflow能解決99%你遇到的問題。

關注信息抓取

登錄就那麼輕鬆的搞定了,用戶信息、微博、粉絲、關注的套路都是一樣,分析攔截的請求,看哪個請求是返回數據的,然後自己模擬這個請求,就ok啦。

這裏就隻做用戶關注的抓取示例,其他的自己去依葫蘆畫瓢。
最近在看環法,自己也是個騎行愛好者...現在天熱...都不能出去騎了,很絕望。
就以環法自行車賽這個用戶的關注信息為例吧

現在你可以借助chrome的工具欄看請求,windows下的快捷鍵是F12

查看他的關注->全部關注。你會發現瀏覽器發了如下的請求

你可以直接複製這個請求,在瀏覽器上打開。

可以看到這個請求返回的就是他的關注用戶的json數據,而python中的json模塊直接解析,很方便吧。

看看這個url[點開試試?]

https://m.weibo.cn/api/container/getIndex?containerid=231051_-_followers_-_5901021570&luicode=10000011&lfid=1005055901021570&featurecode=20000320&type=uid&value=5901021570

該用戶的uid為5901021570,url是怎麼拚的不用再多說了吧。

當然還有分頁,自己往下拖,你可以看到url上會多了個page的參數,那個就是頁號

數據都拿到了,還等什麼?解析完後想怎麼存怎麼存吧。

    def grab_user_follower(self, user_id):
        opener = weibo_http.get_openner()
        user = self.grab_user(user_id)
        att_num = user.attNum
        max_page = int(int(att_num) / 20)
        page = 1
        while page <= max_page:
            resp = opener.open(constants.FOLLOWER_URL_PATTERN % (user_id, user_id, str(page)))
            self.logger.info(constants.FOLLOWER_URL_PATTERN % (user_id, user_id, str(page)))
            r = resp.read()
            resp_json = json.loads(r.decode())
            if 'msg' in resp_json:
                break
            for card in resp_json['cards']:

                for cg in filter(lambda c: 'user' in c, card['card_group']):
                    follower = dao.save_user_info(cg['user'])
                    dao.save_relationship(follower, user)
                    self.id_enqueue(follower.id, self.user_set_lock, self.CRAWLED_USERS, self.user_queue)
            page += 1

後語

關於微博移動端的抓取就暫時說這麼多吧,說實話,移動端還是比較簡單的,多線程可以搞定,隻開了五個,日抓取量已經達到了300W用戶+200W微博了,之後打算改成分布式的...

關於源碼,暫時還沒想放出來,因為不開心,而且我覺得這個也沒什麼難度,基本可以自己動手寫,單線程寫完再改成多線程的,很easy的...

當然我已經放在github了,地址暫時先不放,考驗你找資料的能力了....

過幾天再把網頁版的抓取過程放上來,心情好點再說...

最後更新:2017-07-17 16:44:10

  上一篇:go  ODPS SQL費用估算與控製
  下一篇:go  三個小技巧,讓客戶管理變簡單