什麼是爬蟲?
網路爬蟲
也叫網路蜘蛛
,如果把網際網路比喻成一個蜘蛛網,那麼蜘蛛就是在網上爬來爬去的蜘蛛,爬蟲程式通過請求url地址,根據響應的內容進行解析採集資料,
比如:如果響應內容是html,分析dom結構,進行dom解析、或者正則匹配,如果響應內容是xml/json資料,就可以轉資料物件,然後對資料進行解析。
有什麼作用?
通過有效的爬蟲手段批量採集資料,可以降低人工成本,提高有效資料量,給予運營/銷售的資料支撐,加快產品發展。
業界的情況
目前網際網路產品競爭激烈,業界大部分都會使用爬蟲技術對競品產品的資料進行挖掘、採集、大資料分析,這是必備手段,並且很多公司都設立了爬蟲工程師
的崗位
合法性
爬蟲是利用程式進行批量爬取網頁上的公開資訊,也就是前端顯示的資料資訊。因為資訊是完全公開的,所以是合法的。其實就像瀏覽器一樣,瀏覽器解析響應內容並渲染為頁面,而爬蟲解析響應內容採集想要的資料進行儲存。
反爬蟲
爬蟲很難完全的制止,道高一尺魔高一丈,這是一場沒有硝煙的戰爭,碼農VS碼農
反爬蟲一些手段:
- 合法檢測:請求校驗(useragent,referer,介面加簽名,等)
- 小黑屋:IP/使用者限制請求頻率,或者直接攔截
- 投毒:反爬蟲高境界可以不用攔截,攔截是一時的,投毒返回虛假資料,可以誤導競品決策
- … …
爬蟲基本套路
- 基本流程
- 目標資料
- 來源地址
- 結構分析
- 實現構思
- 操刀編碼
- 基本手段
- 破解請求限制
- 請求頭設定,如:useragant為有效客戶端
- 控制請求頻率(根據實際情景)
- IP代理
- 簽名/加密引數從html/cookie/js分析
- 破解登入授權
- 請求帶上使用者cookie資訊
- 破解驗證碼
- 簡單的驗證碼可以使用識圖讀驗證碼第三方庫
- 破解請求限制
- 解析資料
- HTML Dom解析
- 正則匹配,通過的正規表示式來匹配想要爬取的資料,如:有些資料不是在html 標籤裡,而是在html的script 標籤的js變數中
- 使用第三方庫解析html dom,比較喜歡類jquery的庫
- 資料字串
- 正則匹配(根據情景使用)
- 轉 JSON/XML 物件進行解析
- HTML Dom解析
python爬蟲
- python寫爬蟲的優勢
- python語法易學,容易上手
- 社群活躍,實現方案多可參考
- 各種功能包豐富
- 少量程式碼即可完成強大功能
- 涉及模組包
- 請求
urllib
urllib2
cookielib
- 多執行緒
threading
- 正則
re
- json解析
json
- html dom解析
pyquery
beautiful soup
- 操作瀏覽器
selenium
- 請求
例項解析
鬥魚主播排行
- 目標資料
- 獲取排行榜主播資訊
- 來源地址
- [排行榜地址]
- [主播房間地址]
- https://www.douyu.com/xxx
- xxx=房間號
- https://www.douyu.com/xxx
- 結構分析
- 通過抓包 [排行榜地址],[主播房間地址] (谷歌除錯network/charles/fiddler)
- 獲得排行資料介面:https://www.douyu.com/directory/rank_list/game
- 引數確認(去掉不必要引數)
- cookie確認(去掉不必要cookie)
- 模擬請求(charles/fiddler/postman)
- 獲得主播房間資訊資料
- 發現$ROOM是主播房間資訊,在頁面的script標籤的js變數中,可使用正則工具寫表示式去匹配
- 獲得排行資料介面:https://www.douyu.com/directory/rank_list/game
- 通過抓包 [排行榜地址],[主播房間地址] (谷歌除錯network/charles/fiddler)
- 實現構思
- 通過請求 [主播排行介面] 獲取 [排行榜資料]
- [排行榜資料] 中有主播房間號,可以通過拼接獲得 [主播房間地址]
- 請求 [主播房間地址] 可以獲得 [$ROOM資訊] ,解析可以獲得主播房間資訊
- 操刀編碼
申明:此例子僅作為爬蟲學習DEMO,並無其他利用
基於python實現爬蟲學習基礎demo
def douyu_rank(rankName, statType):
```
鬥魚主播排行資料抓取
[資料地址](https://www.douyu.com/directory/rank_list/game)
* `rankName` anchor(巨星主播榜),fans(主播粉絲榜),haoyou(土豪實力榜),user(主播壕友榜)
* `statType` day(日),week(周),month(月)
```
if not isinstance(rankName, ERankName):
raise Exception("rankName 型別錯誤,必須是ERankName列舉")
if not isinstance(statType, EStatType):
raise Exception("statType 型別錯誤,必須是EStatType列舉")
rankName = `%sListData` % rankName.name
statType = `%sListData` % statType.name
# 請求獲取html原始碼
rs = rq.get(
"https://www.douyu.com/directory/rank_list/game",
headers={`User-Agent`: `Mozilla/5.0`})
# 正則解析出資料
mt = re.search(r`rankListDatas+?=(.*?);`, rs, re.S)
if (not mt):
print u"無法解析rankListData資料"
return
grps = mt.groups()
# 資料轉json
rankListDataStr = grps[0]
rankListData = json.loads(rankListDataStr)
dayList = rankListData[rankName][statType]
# 修改排序
dayList.sort(key=lambda k: (k.get(`id`, 0)), reverse=False)
return dayList
def douyu_room(romm_id):
```
主播房間資訊解析
[資料地址](https://www.douyu.com/xxx)
`romm_id` 主播房號
```
rs = rq.get(
("https://www.douyu.com/%s" % romm_id),
headers={`User-Agent`: `Mozilla/5.0`})
mt = re.search(r`$ROOMs+?=s+?({.*?});`, rs, re.S)
if (not mt):
print u"無法解析ROOM資料"
return
grps = mt.groups()
roomDataStr = grps[0]
roomData = json.loads(roomDataStr)
return roomData
def run():
```
測試爬蟲
```
datas = douyu_rank(ERankName.anchor, EStatType.month)
print `
主播排行榜:`
for item in datas:
room_id = item[`room_id`]
roomData = douyu_room(room_id)
rommName = None
if roomData is not None:
rommName = roomData[`room_name`]
roomInfo = (u`房間(%s):%s` % (item[`room_id`], rommName))
print item[`id`], item[
`nickname`], roomInfo, `[` + item[`catagory`] + `]`
run()
執行結果:
主播排行榜:
無法解析ROOM資料
1 馮提莫 房間(71017):None [英雄聯盟]
2 阿冷aleng丶 房間(2371789):又是我最喜歡的阿冷ktv時間~ [英雄聯盟]
3 勝哥002 房間(414818):勝哥:南通的雨下的我好心累。 [DNF]
4 White55開解說 房間(138286):盧本偉五五開 每天都要很強 [英雄聯盟]
5 東北大鵪鶉 房間(96291):東北大鵪鶉 宇宙第一寒冰 相聲藝術家! [英雄聯盟]
6 老實敦厚的笑笑 房間(154537):德雲色 給兄弟們賠個不是 [英雄聯盟]
7 劉飛兒faye 房間(265438):劉飛兒 月底吃雞 大吉大利 [絕地求生]
8 pigff 房間(24422):【PIGFF】借基地直播,沒OW [守望先鋒]
9 雲彩上的翅膀 房間(28101):翅:還是抽天空套刺激! [DNF]
10 yyfyyf 房間(58428):無盡的9月,殺 [DOTA2]
# 馮提莫 房間做週年主題,解析會有問題