詳解 | Scrapy1.4 知乎模擬登陸 & 爬取首頁、存儲數(shù)據(jù)

開篇.jpg

環(huán)境:win7、pycharm、python3.5
關鍵字:scrapy1.4、mongodb

本文主要分享用scrapy1.4實現(xiàn)知乎模擬郵箱登陸以及將根據(jù)rules匹配的url鏈接爬取(詳見下文zhihu_login.py)數(shù)據(jù)并存入mongodb。
在做之前閱讀了這兩篇文章Python爬蟲(七)--Scrapy模擬登錄Scrapy模擬登陸知乎,但是未能運行成功,文中也沒有處理驗證碼。不過,實現(xiàn)原理大同小異,大家可以參考,本文主要簡單實現(xiàn)并且正確運行,有什么問題還望大家多多指教。謝謝^^
獲取源碼請戳我
下面正文開始:

scrapy_architecture.png

1.cmd命令行下新建項目,詳情請戳我

scrapy startproject zhihu_login_byscrapy
目錄結(jié)構如下:
zhihu_login_byscrapy/
    scrapy.cfg            #項目的配置文件
    zhihu_login_byscrapy/             
        __init__.py
        items.py          # 保存爬取到的數(shù)據(jù)的容器
        pipelines.py      # 項目 pipelines 文件 實現(xiàn)存儲
        settings.py       # 項目的設置文件
        spiders/          # 這里寫你的爬蟲程序
            __init__.py
目錄結(jié)構.png

2.模擬登陸并爬取數(shù)據(jù)zhihu_login.py

在運行前請將代碼中的password 和email 替換

主要邏輯分為:

  • 獲取_xsrf
  • 獲取驗證碼
  • 傳遞入?yún)?shù)post請求處理
  • ItemLoader解析數(shù)據(jù)

涉及的知識可參考官方文檔 +Rule使用+ItemLoader詳解
完整代碼及其注釋如下:

from scrapy.spiders import CrawlSpider, Rule
from scrapy.selector import Selector
from scrapy.linkextractors import LinkExtractor
from scrapy.http import Request, FormRequest
from zhihu_login_byscrapy.items import ZhihuLoginByscrapyItem
import json,time
from PIL import Image
from scrapy.loader import ItemLoader

class ZhihuLoginByscrapy(CrawlSpider):
    name = 'zhihu_scrapy'   #唯一值
    allowed_domains = ["zhihu.com"]
    start_urls = [
        "https://www.zhihu.com"
    ]
    #自動從response中根據(jù)正則表達式提取url,再根據(jù)這個url再次發(fā)起請求,并用callback解析返回的結(jié)果
    #follow修改為True,那么爬蟲會start_urls爬取的頁面中在尋找符合規(guī)則的url,如此循環(huán),直到把全站爬取完畢。 
    rules = (
        Rule(LinkExtractor(allow=('/question/\d+#.*?', )),callback='parse_question',follow=True),
        Rule(LinkExtractor(allow=('/question/\d+',)), callback='parse_question', follow=True),
    )
    #請求頭
    headers = {
        'Accept':'* / *',
        "Accept-Encoding": "gzip, deflate, br",
        "Accept-Language": "zh-CN,zh;q=0.8",
        "Connection": "keep-alive",
        "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
        "Host": "www.zhihu.com",
        "Referer": "https://www.zhihu.com/",
    }

    #程序入口
    def start_requests(self):
        return [Request("https://www.zhihu.com/login/email",headers = self.headers,meta={"cookiejar":1},callback=self.post_login)]

    def post_login(self,response):
        #獲取_xsrf
        _xsrf = Selector(response).xpath('//input[@name="_xsrf"]/@value').extract()[0]
        if _xsrf:
            formdata = {
                '_xsrf': _xsrf,
                'password': '*********',
                'remember_me': 'true',
                'email': '******@*****.com',
                'captcha': ''
            }
            t = str(int(time.time() * 1000))
            captcha_url = 'http://www.zhihu.com/captcha.gif?r=' + t + "&type=login"
            return [Request(captcha_url, headers=self.headers, meta={"cookiejar": response.meta['cookiejar'], "formdata": formdata},
                            callback=self.parse_captcha)]
     #獲取驗證碼
    def parse_captcha(self, response):
        with open('captcha.jpg', 'wb') as f:
            f.write(response.body)
        # Pillow顯示驗證碼
        img = Image.open('captcha.jpg')
        img.show()
        captcha = input('請需要輸入驗證碼: ')
        formdata = response.meta['formdata']
        formdata['captcha'] = captcha
        #登陸成功后, 會調(diào)用after_login回調(diào)函數(shù)
        return [FormRequest("https://www.zhihu.com/login/email",
                            meta={'cookiejar': response.meta['cookiejar']},
                            method='POST',
                            headers=self.headers,
                            formdata=formdata,
                            callback=self.after_login,
                            dont_filter = True,
                              )]

#程序自動使用start_urls構造Request并發(fā)送請求,然后調(diào)用parse函數(shù)對其進行解析,在這個解析過程中使用rules中的規(guī)則從html(或xml)文本中提取匹配的鏈接,通過這個鏈接再次生成Request,如此不斷循環(huán),直到返回的文本中再也沒有匹配的鏈接,或調(diào)度器中的Request對象用盡,程序才停止。
    def after_login(self,response):
        for url in self.start_urls:
            yield Request(url, headers=self.headers, meta={'cookiejar': response.meta['cookiejar']}, dont_filter=True)

    def parse_question(self, response):
        self.logger.info('Hi, this is an item page! %s', response.url)
        item = ItemLoader(item=ZhihuLoginByscrapyItem(), response=response)
        #傳遞 Item 類的字段名稱和對應的 css 解析語法
        item.add_value('url',response.url)
        item.add_css('title','h1.QuestionHeader-title::text')
        item.add_xpath('read', '(//div[@class="NumberBoard-value"])[1]/text()')
        item.add_xpath('focus', '(//div[@class="NumberBoard-value"])[2]/text()')
        item.add_xpath('description', '//span[@class="RichText CopyrightRichText-richText"]/text()')
        print(item)
        return item.load_item()

3.在items.py中定義爬取到數(shù)據(jù)的容器,簡言之就是定義爬取到的數(shù)據(jù)字段,以匹配到的此鏈接為例,獲取問題的url、標題、關注者、瀏覽量、問題描述。

涉及的知識可參考input/output_processor+使用 Item 類轉(zhuǎn)換傳輸數(shù)據(jù)以及ItemLoader 機制解析
完整代碼及其注釋如下(還可以優(yōu)化):

import scrapy
from scrapy.loader.processors import TakeFirst,MapCompose,Join

class ZhihuLoginByscrapyItem(scrapy.Item):
    # define the fields for your item here like:
    url = scrapy.Field(
        output_processor=Join(),
    )  # 問題url
    title = scrapy.Field(
        output_processor=Join(),
    ) # 問題標題
    focus = scrapy.Field(
        output_processor=Join(),
    ) # 問題關注者有多少
    read = scrapy.Field(
        output_processor=Join(),
    ) # 問題瀏覽多少
    description = scrapy.Field(
        output_processor=Join(),
    ) # 問題描述
  1. pipelines.py中將數(shù)據(jù)存儲到json和mongodb中
    涉及的知識可參考: 使用 Pipeline 保存數(shù)據(jù) + 官方文檔mongodb案例+JsonItemExporter參考
from scrapy.exporters import JsonItemExporter
import pymongo

class ZhihuLoginByscrapyPipeline(object):
    # 調(diào)用 scrapy 提供的 json exporter 導出 json 文件
    def __init__(self):
        self.file = open('zhihu_exporter.json', 'wb')
        # 初始化 exporter 實例,執(zhí)行輸出的文件和編碼
        self.exporter = JsonItemExporter(self.file, encoding='utf-8', ensure_ascii=False)
        # 開啟導出
        self.exporter.start_exporting()

    def close_spider(self, spider):
        self.exporter.finish_exporting()
        self.file.close()

    # 將 Item 實例導出到 json 文件
    def process_item(self, item, spider):
        self.exporter.export_item(item)
        return item

class MongoByscrapyPipeline(object):

    def __init__(self, mongo_uri, mongo_db,mongo_col):
        self.mongo_uri = mongo_uri
        self.mongo_db = mongo_db
        self.mongo_col = mongo_col

    @classmethod
    def from_crawler(cls, crawler):
        return cls(
            mongo_uri=crawler.settings.get('MONGO_URI'),
            mongo_db=crawler.settings.get('MONGODB_DB'),
            mongo_col=crawler.settings.get('MONGODB_COLLECTION')
        )

    def open_spider(self, spider):
        self.client = pymongo.MongoClient(self.mongo_uri)
        self.db = self.client[self.mongo_db]

    def close_spider(self, spider):
        # 釋放資源
        self.client.close()

    def process_item(self, item, spider):
        self.db[self.mongo_col].insert_one(dict(item))
        return item

5.settings.py添加如下內(nèi)容
涉及的知識可參考官方文檔settings

#修改USER_AGEN
USER_AGENT = 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36'

DOWNLOAD_DELAY = 4 #下載延遲
#PIPELINES配置,后面數(shù)字代表優(yōu)先級
ITEM_PIPELINES = {
   'zhihu_login_byscrapy.pipelines.MongoByscrapyPipeline': 300,
   'zhihu_login_byscrapy.pipelines.ZhihuLoginByscrapyPipeline': 800,
}
#mongodb配置
MONGODB_SERVER = "localhost"
MONGODB_PORT = 27017
MONGODB_DB = "ana_zhihu"
MONGODB_COLLECTION = "zhihu_scrapy"

6.以上就是涉及到的代碼,下面就是pycharm中執(zhí)行我們的scrapy程序,新建begin.py文件,首先需要按圖示配置好,Script處指定執(zhí)行腳本,最后點擊執(zhí)行即可~
配置.png

begin.py

from scrapy import cmdline
cmdline.execute('scrapy crawl zhihu_scrapy '.split())

7.最后運行效果如下
控制臺print出的item.png
mongodb中存儲的數(shù)據(jù).png
存儲的json數(shù)據(jù).png

8.以上就是所有內(nèi)容,但是尚有需要完善的地方,比如模擬手機登陸、爬蟲job的暫停&執(zhí)行、中間件的使用、代碼優(yōu)化等。

延伸閱讀請戳我

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容