天天看點

scrapy爬蟲之模拟登入豆瓣

##簡介

在之前的博文python爬蟲之模拟登陸csdn使用urllib、urllib2、cookielib及BeautifulSoup等基本子產品實作了csdn的模拟登入,本文通過scrapy模拟登入豆瓣,來深入了解下scrapy。

豆瓣登入需要輸入圖檔驗證碼,我們的程式暫時不支援自動識别驗證碼,需要将圖檔下載下傳到本地并打開以進行人工識别輸入到程式中。

##分析豆瓣登入

1.分析豆瓣登入頁的樣式https://accounts.douban.com/login

scrapy爬蟲之模拟登入豆瓣

從上圖可以看出:

(1).表單的action位址為

https://accounts.douban.com/login
           

(2).驗證碼圖檔的位址為

https://www.douban.com/misc/captcha?id=TqRY2JmFX9EYfLANfLPOURJ5:en&size=s
           

(3).captcha-id值

<input name="captcha-id" value="TqRY2JmFX9EYfLANfLPOURJ5:en" type="hidden">
           

2.分析豆瓣登入頁的form表單登入

通過登入頁我們登入一次,檢視post的資料

scrapy爬蟲之模拟登入豆瓣

是以我們需要通過scrapy提取以下來填充表單:

captcha-id:圖檔驗證碼id

captcha-solution:圖檔驗證碼,我們通過檢視圖檔手動輸入驗證碼

其他如form_email等固定資訊我們可以提前填入表單。

##實作

1.建立爬蟲

source activate scrapy
#使用PIL打開圖檔驗證碼,以便我們識别手動輸入
conda install PIL
pip install Pillow
scrapy genspider douban_login douban.com
           

2.編寫爬蟲

vim douban/spider/douban_login.py
# -*- coding: utf-8 -*-
import scrapy
import urllib
from PIL import Image


class DoubanLoginSpider(scrapy.Spider):
    name = 'douban_login'
    allowed_domains = ['douban.com']
#    start_urls = ['http://www.douban.com/']
    headers = {"User-Agent":"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.221 Safari/537.36 SE 2.X MetaSr 1.0"}

    def start_requests(self):
        '''
        重寫start_requests,請求登入頁面
        '''
        return [scrapy.FormRequest("https://accounts.douban.com/login", headers=self.headers, meta={"cookiejar":1}, callback=self.parse_before_login)]

    def parse_before_login(self, response):
        '''
        登入表單填充,檢視驗證碼
        '''
        print("登入前表單填充")
        captcha_id = response.xpath('//input[@name="captcha-id"]/@value').extract_first()
        captcha_image_url = response.xpath('//img[@id="captcha_image"]/@src').extract_first()
        if captcha_image_url is None:
                print("登入時無驗證碼")
                formdata = {
                                "source": "index_nav",
                                "form_email": "[email protected]",
                                #請填寫你的密碼
                                "form_password": "********",
                        }
        else:
                print("登入時有驗證碼")
                save_image_path = "/home/yanggd/python/scrapy/douban/douban/spiders/captcha.jpeg"
                #将圖檔驗證碼下載下傳到本地
                urllib.urlretrieve(captcha_image_url, save_image_path)
                #打開圖檔,以便我們識别圖中驗證碼
                try:
                        im = Image.open('captcha.jpeg')
                        im.show()
                except:
                        pass
		#手動輸入驗證碼
                captcha_solution = raw_input('根據打開的圖檔輸入驗證碼:')
                formdata = {
                                "source": "None",
                                "redir": "https://www.douban.com",
                                "form_email": "[email protected]",
                                #此處請填寫密碼
                                "form_password": "********",
                                "captcha-solution": captcha_solution,
                                "captcha-id": captcha_id,
                                "login": "登入",
                        }


        print("登入中")
        #送出表單
        return scrapy.FormRequest.from_response(response, meta={"cookiejar":response.meta["cookiejar"]}, headers=self.headers, formdata=formdata, callback=self.parse_after_login)
    def parse_after_login(self, response):
        '''
        驗證登入是否成功
        '''
        account = response.xpath('//a[@class="bn-more"]/span/text()').extract_first()
        if account is None:
                print("登入失敗")
        else:
                print(u"登入成功,目前賬戶為 %s" %account)
           

注意:請将代碼中的登陸賬号密碼改成你自己的。

3.運作爬蟲

#運作爬蟲
scrapy crawl douban_login
           

運作過程中,爬蟲會儲存圖檔到本地并自動打開圖檔,友善我們識别驗證碼并手動輸入:

scrapy爬蟲之模拟登入豆瓣

爬蟲運作的調試資訊主要如下:

#列印部分輸出日志,主要是供我們調試使用
2017-12-28 14:15:31 [scrapy.extensions.logstats] INFO: Crawled 0 pages (at 0 pages/min), scraped 0 items (at 0 items/min)
2017-12-28 14:15:31 [scrapy.extensions.telnet] DEBUG: Telnet console listening on 127.0.0.1:6023
2017-12-28 14:15:32 [scrapy.core.engine] DEBUG: Crawled (404) <GET https://accounts.douban.com/robots.txt> (referer: None)
2017-12-28 14:15:32 [scrapy.core.engine] DEBUG: Crawled (200) <GET https://accounts.douban.com/login> (referer: None)
登入前表單填充
登入時有驗證碼
根據打開的圖檔輸入驗證碼:needle
登入中
2017-12-28 14:15:39 [scrapy.downloadermiddlewares.redirect] DEBUG: Redirecting (302) to <GET https://www.douban.com> from <POST https://accounts.douban.com/login>
2017-12-28 14:15:39 [scrapy.core.engine] DEBUG: Crawled (200) <GET https://www.douban.com/robots.txt> (referer: None)
2017-12-28 14:15:40 [scrapy.core.engine] DEBUG: Crawled (200) <GET https://www.douban.com> (referer: https://accounts.douban.com/login)
登入成功,目前賬戶為 三頁的帳号
           

最後列印的資訊為"登入成功,目前賬戶為 三頁的帳号",顯示出我們的豆瓣賬号名稱,說明scrapy模拟登入豆瓣是成功的。

##問題處理

在實作模拟登入過程中,碰到過幾個問題在次也和大家分享下:

1.設定scrapy的user_agent

在博文scrapy爬蟲之《琅琊榜2》話題title收集及詞雲展示我們是在settings中設定,本文是在爬蟲中通過以下設定:

headers = {"User-Agent":"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.221 Safari/537.36 SE 2.X MetaSr 1.0"}
           

2.scrapy 導入PIL報錯如下:

>>> import PIL
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/yanggd/miniconda2/envs/scrapy/lib/python2.7/site-packages/PIL/__init__.py", line 14, in <module>
    from . import version
ImportError: cannot import name version

#解決方案:
pip install Pillow
           

3.scrapy提示以下錯誤

DEBUG:Filtered offsite request to 'accounts.douban.com': <POST https://accounts.douban.com/login>
           

原因:start_requests中的 url的域名不能和檔案中自己配置的allowed_domains不一緻,否則會被過濾掉。我之前的設定為:

allowed_domains = ['www.douban.com']
           

而送出表單請求的url為"https://accounts.douban.com/login",是以出現問題。

解決辦法可以通過停用過濾功能,添加"dont_filter=True",如下所示:

return scrapy.FormRequest.from_response(response, meta={"cookiejar":response.meta["cookiejar"]}, headers=self.headers, formdata=formdata, callback=self.parse_after_login, dont_filter=True)
           

或者

與allowed_domains設定一緻,如:

allowed_domains = ['douban.com']
           

如果你對博文感興趣,請關注我的公衆号“木讷大叔愛運維”,與你分享運維路上的點滴。

scrapy爬蟲之模拟登入豆瓣

繼續閱讀