使用代理處理反爬抓取陌陌文章
優(yōu)采云 發(fā)布時(shí)間: 2020-06-06 08:01使用代理反爬抓取陌陌文章,獲取文章標題、內容、公眾號等信息,并儲存到MongoDB數據庫中。
如果要抓取微信公眾號文章可以使用搜狗的搜索引擎,它會(huì )顯示最新的文章,但是有兩個(gè)問(wèn)題須要你們注意:
關(guān)于代理池的實(shí)現以及使用可以參考這篇文章:使用Redis+Flask維護動(dòng)態(tài)代理池
下圖展示了具體的流程框架:
/img/remote/36680
def parse_index(html):
doc = pq(html)
items = doc('.news-box .news-list li .txt-box h3 a').items()
for item in items:
yield item.attr('href')def parse_index(html):
doc = pq(html)
items = doc('.news-box .news-list li .txt-box h3 a').items()
for item in items:
yield item.attr('href')
在流程框架部份我們談到,在此即將使用搜狗搜索陌陌站點(diǎn)文章,首先使我們步入搜狗搜索界面,比如輸入關(guān)鍵字景色,就會(huì )出現陌陌文章的列表。
/img/remote/36681?w=1042&h=552
從網(wǎng)頁(yè)的url可以看出這是一個(gè)get懇求,只保留主要參數,可以把url簡(jiǎn)化為
/img/remote/36682
其中,“query”代表搜索的關(guān)鍵詞,“type”代表搜索結果的類(lèi)型,“type=1”表示搜索結果是微信公眾號,“type=2”表示搜索結果是陌陌文章,“page”也就是當前頁(yè)數。
分析完網(wǎng)頁(yè)的url組成以后,我們先解決第一個(gè)問(wèn)題:保存cookie,模擬登陸。
打開(kāi)瀏覽器控制臺,選擇NetWork->Headers選項卡,可以看見(jiàn)懇求的headers信息。
/img/remote/36683
解決完以上問(wèn)題以后,讓我們嘗試寫(xiě)一下代碼獲取第1-100頁(yè)的網(wǎng)頁(yè)源碼:
from urllib.parse import urlencode
import requests
base_url = ''
# 構造請求頭
headers = {
'Cookie': 'CXID=DF1F2AE56903B8B6289106D60E0C1339; SUID=F5959E3D8483920A000000005BCEB8CD; sw_uuid=3544458569; ssuid=8026096631; pex=C864C03270DED3DD8A06887A372DA219231FFAC25A9D64AE09E82AED12E416AC; SUV=00140F4F78C27EE25BF168CF5C981926; ad=p7R@vkllll2bio@ZlllllVsE@EclllllNBENLlllll9lllllpA7ll5@@@@@@@@@@; IPLOC=CN4110; ABTEST=2|1543456547|v1; weixinIndexVisited=1; sct=1; JSESSIONID=aaaXtNmDWRk5X5sEsy6Cw; PHPSESSID=lfgarg05due13kkgknnlbh3bq7; SUIR=EF72CF750D0876CFF631992E0D94BE34;',
'Host': '',
'Upgrade-Insecure-Requests': '1',
'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36'
}
def get_html(url, count=1):
response = requests.get(url, allow_redirects=False, headers=headers)
# 判斷網(wǎng)頁(yè)返回的狀態(tài)碼是否正常
# 如果狀態(tài)碼是200說(shuō)明可以正常訪(fǎng)問(wèn)
if response.status_code == 200:
return response.text
# 如果狀態(tài)碼是302,則說(shuō)明IP已經(jīng)被封
if response.status_code == 302:
return None
def get_index(keyword, page):
data = {
'query': keyword,
'type': 2,
'page': page
}
queries = urlencode(data)
url = base_url + queries
html = get_html(url)
return html
def main():
for page in range(1, 101):
html = get_index('風(fēng)景', page)
print(html)
if __name__ == '__main__':
main()
運行以上代碼,會(huì )發(fā)覺(jué)剛開(kāi)始運行正常,正確返回網(wǎng)頁(yè)源碼,之后便始終返回None,此時(shí)使我們打開(kāi)瀏覽器觀(guān)察一下:
/img/remote/36684
可以看出,代碼運行后不停返回None的緣由是網(wǎng)頁(yè)被重定向,需要輸入驗證碼能夠正常訪(fǎng)問(wèn),這便是我們開(kāi)篇說(shuō)過(guò)的第二個(gè)問(wèn)題,我們的訪(fǎng)問(wèn)被搜狗搜索的反爬蟲(chóng)舉措攔截,如果想要繼續正常訪(fǎng)問(wèn),便須要借助代理池獲取隨機代理來(lái)繞開(kāi)反爬機制。
在使用Redis+Flask維護動(dòng)態(tài)代理池一文中,我們講解了代理池的基本原理和簡(jiǎn)單實(shí)現,代碼已托管到github上,現在使我們借助代理池來(lái)獲取隨機代理。
首先使我們定義get_proxy()方法,返回代理池獲取的隨機可用ip:
# flask*敏*感*詞*的是5000端口
PROXY_POOL_URL = ':5000/get'
def get_proxy():
try:
response = requests.get(PROXY_POOL_URL)
if response.status_code == 200:
return response.text
return None
except ConnectionError:
return None
接下來(lái)更改get_html(url, count=1)方法,以隨機ip的形式訪(fǎng)問(wèn)網(wǎng)頁(yè):
MAX_COUNT = 5
proxy = None
def get_html(url, count=1):
# 打印抓取的url
print('Crawling', url)
# 打印嘗試抓取的次數
print('Trying Count', count)
global proxy
# 如果抓取的次數大于最大次數,則返回None
if count >= MAX_COUNT:
print('Tried Too Many Counts')
return None
try:
if proxy:
proxies = {
'http': 'http://' + proxy
}
# allow_redirects=False:禁止瀏覽器自動(dòng)處理302跳轉
response = requests.get(url, allow_redirects=False, headers=headers, proxies=proxies)
else:
response = requests.get(url, allow_redirects=False, headers=headers)
if response.status_code == 200:
return response.text
# 狀態(tài)碼是302,說(shuō)明IP已經(jīng)被封,調用get_proxy()獲取新的ip
if response.status_code == 302:
# Need Proxy
print('302')
proxy = get_proxy()
if proxy:
print('Using Proxy', proxy)
return get_html(url)
else:
print('Get Proxy Failed')
return None
except ConnectionError as e:
# 如果連接超時(shí),重新調用get_html(url, count)方法
print('Error Occurred', e.args)
proxy = get_proxy()
count += 1
return get_html(url, count)
再次運行代碼,會(huì )發(fā)覺(jué)不停重復復印None的情況基本消失。大家注意,這里是基本消失,原因是我們的代理池使用的是免費代理網(wǎng)站獲取的代理搜狗微信 反爬蟲(chóng),同一時(shí)刻可能會(huì )有許多人訪(fǎng)問(wèn),這樣就很容易導致ip地址被封的情況。如果你想要獲取更好的療效,不妨使用一下收費代理。
至此,我們解決了開(kāi)篇提及的兩個(gè)問(wèn)題,接下來(lái),就可以抓取網(wǎng)頁(yè),分析內容。
首先我們須要獲取到第1-100頁(yè)中每一篇文章的url:
/img/remote/36685
def parse_index(html):
doc = pq(html)
items = doc('.news-box .news-list li .txt-box h3 a').items()
for item in items:
yield item.attr('href')
def main():
for page in range(1, 101):
html = get_index(KEYWORD, page)
if html:
article_urls = parse_index(html)
print(article_urls)
獲取到每一篇文章的url以后,就須要解析每一篇文章的內容。解析方式與前面相同,在此不再贅言。具體代碼如下:
def parse_detail(html):
try:
doc = pq(html)
title = doc('.rich_media_title').text()
content = doc('.rich_media_content ').text()
date = doc('#publish_time').text()
nickname = doc('.rich_media_meta_list .rich_media_meta_nickname').text()
wechat = doc('#activity-name').text()
return {
'title': title,
'content': content,
'date': date,
'nickname': nickname,
'wechat': wechat
}
except XMLSyntaxError:
return None
需要注意的一點(diǎn)就是須要捕獲XMLSyntaxError異常。
最后使我們新建一個(gè)config.py文件,文件中包含了MongoDB的URL搜狗微信 反爬蟲(chóng),數據庫名稱(chēng),表名稱(chēng)等常量:
MONGO_URL = 'localhost'
MONGO_DB = 'weixin'
在spider.py中配置儲存到MongoDB相關(guān)方式:
from config import *
import pymongo
client = pymongo.MongoClient(MONGO_URL)
db = client[MONGO_DB]
def save_to_mongo(data):
if db['articles'].update({'title': data['title']}, {'$set': data}, True):
print('Saved to Mongo', data['title'])
else:
print('Saved to Mongo Failed', data['title'])
運行代碼,接下來(lái)在MongoDB中進(jìn)行查看:
/img/remote/36686
項目完整代碼已托管到github: