隨著反爬的技術(shù)深入,特別是有關(guān)驗(yàn)證碼返回,這對與初學(xué)者來說無疑是遇到了懸崖,原本想開開心心的登錄某個網(wǎng)站并爬取自己需要的信息,可沒想到有些網(wǎng)站當(dāng)你在某一段時間連續(xù)登陸幾次之后他就返回驗(yàn)證碼驗(yàn)證一下是不是人工所為,這對于初學(xué)者來說無疑是撥了一盤冷水,網(wǎng)站之所以這樣做無非就是防止爬蟲爬取他的信息,那么作為爬蟲的開發(fā)者,我們就是使用爬蟲去爬去他的信息,也就是說我們爬蟲者是時時刻刻都在和個站站長斗智斗勇;好了,說了那么多,我們開始進(jìn)入正題吧:今天的任務(wù)就是 登錄豆瓣之后爬取里面的個人發(fā)布過的日記信息-日記標(biāo)題、標(biāo)題鏈接、發(fā)布時間、日記內(nèi)容等
在開始之前,我們先了解下當(dāng)遇到驗(yàn)證碼時,在實(shí)際項(xiàng)目中我們有哪些處理方式,直說了,一般有三種方式:
1) 手動輸入驗(yàn)證碼
2) 通過編寫程序自動識別驗(yàn)證碼-(這里會涉及到人工智能的圖像識別)
3) 通過一些打碼接口實(shí)現(xiàn),比如打碼兔等平臺,讓別人通過接口幫我們輸入驗(yàn)證碼,但是需要支付一定的費(fèi)用
在此,我們將以第一種方式進(jìn)行講解案例的使用,首先,我們來到豆瓣官網(wǎng)的登錄界面:https://accounts.douban.com/login?alias=&redir=https%3A%2F%2Fwww.douban.com%2F&source=index_nav&error=1001,此時不同瀏覽器會有不同的提示,如果有一行提示語點(diǎn)擊“登錄”超鏈接即可來到登錄頁面,然后輸入一個錯誤的賬戶(為了看到深層的登錄鏈接以及登錄參數(shù)),效果圖如下:

18.png
從這個效果圖中顯示沒有驗(yàn)證碼,這很正常,因?yàn)榫W(wǎng)站只有懷疑我們是爬蟲時才返回的,然后我們把隱藏的post請求的連接復(fù)制一份粘貼到網(wǎng)址中訪問,此時可能會出現(xiàn)有驗(yàn)證碼的登錄頁面,然后查看源碼會有以下效果圖:

19.png
接著我們輸入一個正確的用戶名和密碼以及驗(yàn)證碼(注意:此時是需要開發(fā)開發(fā)者模式的),成功登陸之后我們找到 post 請求的鏈接并點(diǎn)擊,如圖:

20.png
我們登陸成功后會自動跳轉(zhuǎn)到主頁,其鏈接為:https://www.douban.com/people/178279892/,效果圖如下:

30.png
因?yàn)槲覀兇龝盒枰D(zhuǎn)到日記頁面中獲取發(fā)布過的日記信息,因此我們必須想辦法獲取上圖中的日記頁面鏈接,經(jīng)過觀察,我們可以通過 xpath 表達(dá)式獲取:
//a[@id="usr-profile-nav-notes"]/@href
先觀察到這里,接下來我們先創(chuàng)建一個爬蟲項(xiàng)目先,使用創(chuàng)建的命令為:scrapy startproject doubanloginproject
然后基于 basic 模板創(chuàng)建一個爬蟲文件,命令為: scrapy genspider -t basic doubanspider douban.com
首先我們打開 items.py 定義我們需要存放數(shù)據(jù)的變量:
import scrapy
class DoubanloginprojectItem(scrapy.Item):
name = scrapy.Field() #日記標(biāo)題
href = scrapy.Field() #日記標(biāo)題鏈接
time = scrapy.Field() #日記發(fā)布時間
content = scrapy.Field() #日記內(nèi)容
根據(jù)以上效果圖的分析,我們開始編寫我們的爬蟲文件:
# -*- coding: utf-8 -*-
import scrapy
import urllib.request
# from scrapy.http import FormRequest
from doubanloginproject.items import DoubanloginprojectItem
#知乎-會話:https://www.zhihu.com/question/54773510
class DbloginspiderSpider(scrapy.Spider):
name = 'dbloginspider'
allowed_domains = ['douban.com']
start_urls = ['https://accounts.douban.com/login']
def parse(self, response):
#首先獲取驗(yàn)證圖片地址并復(fù)制給 imgurl 變量
imgurl = response.xpath('//img[@id="captcha_image"]/@src').extract()
#由于驗(yàn)證碼時有時無,因此需要判斷如果有就手動輸入
if len(imgurl) > 0:
print("有驗(yàn)證碼返回...")
#將驗(yàn)證圖片保存到本地中
local_path = r"C:/Users/Administrator/Desktop/yanzhangma/lcy.png"
urllib.request.urlretrieve(imgurl[0], filename=local_path)
#定義接受驗(yàn)證碼變量
capt_value = input("請查看本地 lcy.png 圖片并輸入對應(yīng)的驗(yàn)證碼-> ")
#設(shè)置帶有驗(yàn)證碼的 post 信息
data = {
"redir":"https://www.douban.com/people/178279892/",#要跳轉(zhuǎn)的鏈接
"form_email":"12345678@163.com",#用戶名
"form_password":"luchangyin",#密碼
"captcha-solution":capt_value,#驗(yàn)證碼
}
else:#此時不需要驗(yàn)證碼
print("無驗(yàn)證碼登錄...")
#設(shè)置無驗(yàn)證碼請求的參數(shù)
data = {
"redir":"https://www.douban.com/people/178279892/",#要跳轉(zhuǎn)的鏈接
"form_email":"12345678@163.com",#用戶名
"form_password":"luchangyin",#密碼
}
print("登錄中......")
#帶參的登錄請求
return [scrapy.FormRequest.from_response(response,
#設(shè)置 cookie 信息 注:這兩項(xiàng)在 settings.py 文件中設(shè)置即可
#meta={"cookiejar":response.meta["cookiejar"]}, #如果重寫 start_requests()方法,那么該值必須對應(yīng)請求里的 meta 中的鍵
#設(shè)置請求頭模擬成瀏覽器
#headers=self.headers,
#設(shè)置 post 表單中的數(shù)據(jù)
formdata=data,
#設(shè)置回調(diào)函數(shù)
callback=self.mynode,
)]
#之所以創(chuàng)建這個方法是因?yàn)槿绻苯哟蜷_日記鏈接:https://www.douban.com/people/178279892/notes 的話,不需要 cookie 也可以訪問,因此為看到 cookie 的效果我們定義該方法
def mynode(self, response):
#獲取用戶名
my_name = response.xpath(r'//div[@class="info"]/h1/text()').extract_first()
#獲取日記頁面的鏈接
note_url = response.xpath(r'//a[@id="usr-profile-nav-notes"]/@href').extract_first()
#回調(diào)方法處理的是請求之后的數(shù)據(jù)
return scrapy.Request(note_url, callback = self.next)
到這里的話,為了驗(yàn)證登錄之后會怎么樣,建議妳在 mynode() 方法中不要就那么快的去請求日記界面鏈接,最好先把響應(yīng)結(jié)果先存到本地的 html 中,然后打開看有木有數(shù)據(jù),在這里我就不做演示了;接下來我們繼續(xù),看看日記頁面需要怎樣獲取相應(yīng)的信息,看圖:

31.png
通過以上方法的請求跳轉(zhuǎn),我們來到了日記數(shù)據(jù)頁面,根據(jù)效果圖的分析結(jié)果,我們再寫個方法獲取日記信息:
#獲取日記信息方法
def next(self, response):
print("......此時已經(jīng)登錄完成并爬去了個人中心的日記數(shù)據(jù)......")
#獲取日記選項(xiàng)列表
data_list = response.xpath(r'//div[@class="note-container"]')
item = DoubanloginprojectItem() #獲取實(shí)體對象
for data in data_list:
item["name"] = data.xpath(r'./div[1]/h3/a/text()').extract() #日記標(biāo)題
item["href"] = data.xpath(r'./div[1]/h3/a/@href').extract() #日記鏈接
item["time"] = data.xpath(r'./div[1]/div/span/text()').extract()#日記發(fā)布時間
item["content"] = data.xpath(r'./div[3]/text()').extract() #日記內(nèi)容
yield item
到目前為止,爬蟲文件搞定了,為了以防萬一,建議在循環(huán)中先輸出下數(shù)據(jù)看看有木有值,這里不做演示;好了,爬蟲文件搞定之后接下來我們就要對這些數(shù)據(jù)進(jìn)行處理,自當(dāng)來到管道文件 pipelines.py :
import csv
class DoubanloginprojectPipeline(object):
def __init__(self):
self.f = open("doubannote.csv", "w")
self.writer = csv.writer(self.f)
self.writer.writerow(['日記標(biāo)題', '日記鏈接', '日記發(fā)布時間', '日記內(nèi)容'])
def process_item(self, item, spider):
#循環(huán)寫入本地文件
for i in range(0, len(item["name"])):
#保存為csv文件
dbnote_list = [item["name"][i], item["href"][i], item["time"][i], item["content"][i]]
print("管道文件測試-> %s"%dbnote_list)#輸出測試
self.writer.writerow(dbnote_list)
return item
def close_spider(self, spider):
self.f.close()
最后一步,別忘了在配置文件中啟動管道以及相應(yīng)信息的設(shè)置哦:
# Crawl responsibly by identifying yourself (and your website) on the user-agent
#模擬瀏覽器
USER_AGENT = 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.186 Safari/537.36'
# Obey robots.txt rules 不遵循網(wǎng)站規(guī)則
ROBOTSTXT_OBEY = False
# Disable cookies (enabled by default) 打開 cookie,當(dāng)然默認(rèn)是打開的
COOKIES_ENABLED = True
#啟動管道文件
ITEM_PIPELINES = {
'doubanloginproject.pipelines.DoubanloginprojectPipeline': 300,
}
萬事俱備,我們開始破東風(fēng)吧,執(zhí)行爬蟲文件:scrapy crawl dbloginspider --nolog
大致有兩種提示:一種是沒有驗(yàn)證碼,一種是有驗(yàn)證碼:
1)無驗(yàn)證碼

23.png
2)有驗(yàn)證碼

24.png
打開我們本地的 csv 文件:

26.png
圖中可以看到爬取下來的表格中的數(shù)據(jù)后面無意中多了一個空行,只需在管道文件中修改下open()函數(shù)即可:
self.f = open("blogs.csv", "w", newline='') #newline=''去掉多余的空行