作為科研狗袜刷,新浪微博一生黑。一開(kāi)始打算花錢(qián)買(mǎi)他們的商業(yè)API愕宋,結(jié)果跟我說(shuō)不跟科研機(jī)構(gòu)合作玻靡,我也是日了狗了。后來(lái)費(fèi)盡千辛萬(wàn)苦寫(xiě)了個(gè)爬蟲(chóng)中贝,差點(diǎn)沒(méi)把我小號(hào)封了手動(dòng)再見(jiàn).gif
本來(lái)寫(xiě)字的陣地主要在lofter囤捻,結(jié)果lofter這坑貨不支持代碼高亮,讓我這個(gè)偽碼農(nóng)如何自處邻寿?好了蝎土,閑話少敘已經(jīng)敘了不少,把我這三天的奮斗結(jié)果稍稍記錄一下绣否。
一些學(xué)習(xí)資料
- Fiddler簡(jiǎn)易使用教程 抓cookies用(必看)
- PySpider簡(jiǎn)易教程 整個(gè)爬蟲(chóng)用到的框架(必看)
- HTTP Header入門(mén)詳解 在模擬登錄過(guò)程中要涉及到http頭的設(shè)置誊涯,需要了解基本信息
- 全程模擬新浪微博登錄2015 這個(gè)分析了新浪微博網(wǎng)頁(yè)版現(xiàn)在還在用的登錄過(guò)程,其中使用到的最新腳本ssologin.js版本號(hào)1.4.18蒜撮。不過(guò)本文主要是基于wap版的爬蟲(chóng)暴构,這個(gè)沒(méi)必要看,如果想進(jìn)一步爬取網(wǎng)頁(yè)版的微博可以參考學(xué)習(xí)淀弹。
- Sina微博爬取@pyspider 這篇文章通過(guò)用戶名和密碼獲取wap版微博的cookies后再進(jìn)行后續(xù)操作丹壕,也可參考。
-
PyQuery文檔
&&
CSS選擇器&&
Python正則表達(dá)式re package文檔
Python
頁(yè)面爬下來(lái)以后用于操作頁(yè)面元素獲取需要的內(nèi)容(必看)
獲取cookies用于模擬登陸
參考學(xué)習(xí)資料1里面設(shè)置好fiddler薇溃,然后打開(kāi)http://weibo.cn/ 沒(méi)登陸的話登陸菌赖。在登錄的狀態(tài)下打開(kāi)你要進(jìn)行爬取的頁(yè)面,觀察fiddler里抓到的包沐序,得到需要的cookies信息
對(duì)應(yīng)設(shè)置好PySpider里的
crawl_config
琉用,需要注意的是cookies
和headers
堕绩,考慮到我的代碼邏輯我把這兩個(gè)都放在crawl_config
里而不是具體的爬取函數(shù)里。關(guān)于頭的設(shè)置也可以參考fiddler里Headers
一欄邑时,完整代碼貼在最后可以參考我的設(shè)置奴紧。
爬蟲(chóng)邏輯
我的需求是對(duì)于指定的用戶,給出用戶主頁(yè)(形如http://weibo.cn/kaikai0818
或http://weibo.cn/u/1788136742
)晶丘,爬取TA全部的微博及相關(guān)信息(如發(fā)布時(shí)間黍氮、轉(zhuǎn)發(fā)數(shù)等)。
入口函數(shù)on_start
沒(méi)什么可說(shuō)的浅浮,因?yàn)樵O(shè)置了全局的headers和cookies這邊在self.crawl
的時(shí)候就不需要再發(fā)送了沫浆,如果沒(méi)設(shè)置成全局的,必須在這邊設(shè)置并發(fā)送滚秩。
index_page
函數(shù)主要用來(lái)計(jì)算一共有多少頁(yè)需要爬取专执,頁(yè)面有一個(gè)類型為hidden
的<input>
元素記錄了一共有多少頁(yè),然后一個(gè)for得到所有需要爬取的頁(yè)面地址郁油。
list_single_page
用于處理每一頁(yè)微博的內(nèi)容本股,一頁(yè)有十條,主要就是操作頁(yè)面獲取單條微博的地址(因?yàn)楹罄m(xù)需求可能需要爬取每一條微博的轉(zhuǎn)發(fā)和評(píng)論內(nèi)容)桐腌。這邊有個(gè)trick拄显,和web版微博不同的是,wap版單條微博并沒(méi)有一個(gè)所謂的地址哩掺,只有一個(gè)轉(zhuǎn)發(fā)或者評(píng)論的地址凿叠,代碼里用了轉(zhuǎn)發(fā)的地址涩笤。值得注意的是我自己測(cè)試這個(gè)爬蟲(chóng)的時(shí)候爬了1500條微博左右的時(shí)候賬號(hào)被凍結(jié)了嚼吞,建議在dashboard調(diào)一下rate、burst值蹬碧。不過(guò)后來(lái)登錄后激活了一下賬號(hào)又能用了舱禽,還沒(méi)測(cè)試過(guò)多少值可以避免被凍,因?yàn)椴磺宄吕藘鼋Y(jié)的機(jī)制恩沽,我的小號(hào)還要用來(lái)花癡的誊稚,不敢瞎搞了(:з」∠)
detail_page
用來(lái)處理單條微博,然后就是css選擇器+正則的應(yīng)用了哇罗心,沒(méi)什么可說(shuō)的里伯,就是找規(guī)律,目前測(cè)試過(guò)了沒(méi)啥問(wèn)題渤闷,不過(guò)爬下來(lái)的1500多條也沒(méi)認(rèn)真檢查過(guò)疾瓮,大家自己再看看了哇。
一個(gè)能跑的代碼如下:
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
# Created on 2016-03-07 13:26:00
# Project: user_timeline
import re
import time
from pyquery import PyQuery
from pyspider.libs.base_handler import *
class Handler(BaseHandler):
user_url = "http://weibo.cn/kaikai0818"
crawl_config = {
'itag': 'v1',
'headers': {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:44.0) Gecko/20100101 Firefox/44.0',
"Host": "weibo.cn",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"Accept-Language": "zh-CN,zh-TW;q=0.8,zh-HK;q=0.6,en-US;q=0.4,en;q=0.2",
"Accept-Encoding": "gzip, deflate",
"DNT": "1",
"Connection": "keep-alive"
},
'cookies': {
"_T_WM":"I?kkw(ˉ﹃ˉ)",
"SUB":"I?kkw(ˉ﹃ˉ)",
"gsid_CTandWM":"I?kkw(ˉ﹃ˉ)",
"_T_WL":"1",
"_WEIBO_UID":"I?kkw(ˉ﹃ˉ)"
}
}
@every(minutes=24 * 60)
def on_start(self):
self.crawl(Handler.user_url, callback=self.index_page,method="GET")
@config(age=1 * 24 * 60 * 60)
def index_page(self, response):
#計(jì)算該用戶的微博共有幾頁(yè)
pages = response.doc("[type=hidden]").attr["value"]
for i in range(1,int(pages)+1):
self.crawl(Handler.user_url+"?page="+str(i), callback=self.list_single_page,method="GET")
@config(priority=2) #數(shù)字越大優(yōu)先級(jí)越高
def list_single_page(self, response):
#處理當(dāng)前頁(yè)的微博
url = "http://weibo.cn/repost/"
for each in response.doc("div[id^=\"M_\"]").items():
mid = each.attr("id")
self.crawl(url + mid[2:], cookies = response.cookies, callback=self.detail_page)
#self.crawl可加上參數(shù)exetime=time.time() + 1*60 (即一分鐘后再crawl)
@config(priority=1)
def detail_page(self, response):
res = response.doc("#M_")
#判斷是否為轉(zhuǎn)發(fā)
if len(res.find("span.cmt")) != 0:
is_rt = 1
else:
is_rt = 0
#判斷是否含圖片
if len(res.children("div").eq(1).find("img")) != 0:
has_pic = 1
else:
has_pic = 0
if is_rt == 0:
text = res.find("span.ctt").text()
orig_user = "NA"
orig_text = "NA"
else:
orig_user = res("div:first-child span.cmt a").text()
orig_text = res.find("span.ctt").text()
td = re.findall(r'</span>([\s\S]+)<span class=',res("div:last-child").html())
text = PyQuery(td[0]).text()
return {
"screen_name":res("div:first-child > a:first-child").text(),
"time":res.find("span.ct").text(),
"text":text,
"is_rt":is_rt,
"has_pic":has_pic,
"orig_user":orig_user[1:],
"orig_text":orig_text,
"repo_cnt":re.search('\d+',response.doc("span.pms").text()).group(),
"cmt_cnt":re.search('\d+',response.doc("span.pms").siblings().eq(0).text()).group(),
"atti_cnt":re.search('\d+',response.doc("span.pms").siblings().eq(1).text()).group()
}
這輩子干得最癡漢的事情就是寫(xiě)了個(gè)爬蟲(chóng)把人一千多條微博扒拉下來(lái)了飒箭,感覺(jué)以后再也不會(huì)這么用力愛(ài)一個(gè)人了→_→
……
……
……
好了狼电,我編不下去了蜒灰,這就是科研狗的日常。doge.jpg
doge.jpg