Scrapy框架應用實踐


參考原文:
https://www.jianshu.com/p/6bc5a4641629
https://scrapy-chs.readthedocs.io/zh_CN/latest/intro/overview.html

安裝

  • pip install scrapy

創建項目

  • 創建一個名爲tutorial文件夾
    cmd: scrapy startproject tutorial

文件說明

  • scrapy.cfg:配置文件
  • spiders:存放Spider文件(爬取的py文件)
  • items.py:保存爬取數據的容器,和字典較像
  • middlewares.py:定義Downloader Middlewares(下載器中間件)和Spider Middlewares(蜘蛛中間件)的實現
  • pipelines.py:定義Item Pipeline的實現,實現數據的清洗,儲存,驗證。
  • settings.py:全局配置(代理設置等)

創建一個spider(爬蟲程序)

  • 包含了一個用於下載的初始URL,如何跟進網頁中的鏈接以及如何分析頁面中的內容, 提取生成 item 的方法。

  • cmd:scrapy genspider xiaoshuo(py名) https://www.xiaoshuodaquan.com/quanben/(URL)

生成文件xiaoshuo.py

  • name:項目名稱,用於區別Spider
  • allowed_domains:是允許爬取的域名,比如一些網站有相關鏈接,域名就和本網站不同,這些就會忽略。
  • start_urls:Spider爬取的網站,定義初始的請求url,可以多個。
  • parse方法:是Spider的一個方法,在請求start_url後,這個方法對網頁解析,與提取自己想要的東西。
  • response參數:是請求網頁後返回的內容,也就是你需要解析的網頁。 被調用時,每個初始URL完成下載後生成的 Response 對象將會作爲唯一的參數傳遞給該函數。 該方法負責解析返回的數據(response data),提取數據(生成item)以及生成需要進一步處理的URL的 Request 對象。
# xiaoshuo.py
import scrapy

class XiaoshuoSpider(scrapy.Spider):
    name = 'xiaoshuo'
    allowed_domains = ['https://www.xiaoshuodaquan.com/quanben/']
    start_urls = ['http://https://www.xiaoshuodaquan.com/quanben//']

    def parse(self, response):
        pass

Post方式帶參數請求網頁

從Network header中構建

# 構造請求頭, 對setting中的文件內容進行覆蓋
custom_settings = {
    "DEFAULT_REQUEST_HEADERS": {
        'Host': '',
        'Connection': 'keep-alive',
        'Accept': '*/*',
        'Origin': '',
        'X-Requested-With': 'XMLHttpRequest',
        'User-Agent': '',
        'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
        'Referer': '',
        'Accept-Encoding': '',
        'Accept-Language': '',
        'Cookie': ''
    }
}
def form_data_func(self, pageNum):
    # 數據
    formData = {
        'pageNum': str(pageNum),
        'pageSize': str(30),
        'column': 'szse',
        'tabName': 'fulltext',
        'plate': 'sz;sh',
        'category': 'category_ndbg_szsh',
        'seDate': '2017-04-02~2020-04-02',
        'isHLtitle': 'true'
    }
    return formData
 
# 重寫start_requests
def start_requests(self):
    # 請求的地址
    url = "http://www.cninfo.com.cn/new/hisAnnouncement/query/"

    # 模擬表單或Ajax提交post請求
    yield scrapy.FormRequest(url, formdata=self.form_data_func(1), callback=self.pageNum_parse)
  • Scrapy爲Spider的 start_urls 屬性中的每個URL創建了 scrapy.Request 對象,並將 parse 方法作爲回調函數(callback)賦值給了Request。
  • Request對象經過調度,執行生成 scrapy.http.Response 對象並送回給spider parse() 方法。

定義item

  • item是保存爬取數據的容器,使用的方法和字典差不多。
# item.py
# 定義想要提取的信息
class TutoriaItem(scrapy.Item):
    # define the fields for your item here like:
    # name = scrapy.Field()
    hreflink = scrapy.Field()  # 鏈接
    title = scrapy.Field()  # 書名
    word_cnt = scrapy.Field()  # 總字數

再次打開spider來提取我們想要的信息

# spider: xiaoshuo.py
item = TutoriaItem()
yield item

setting

  • 代理設置
# Setting.py
USER_AGENT = 'xiaoshuo (+代理IP)'
  • Obey robots.txt rules
ROBOTSTXT_OBEY = False
  • File下載並存儲配置
# pipeline優先級設置
ITEM_PIPELINES = {
    'juchao.pipelines.clearDataPipeline':1,
    # 'scrapy.pipelines.files.FilesPipeline':1,
    'juchao.pipelines.JuchaoPipeline': 2,
#    'juchao.pipelines.JuchaoPipeline': 300,
}
FILES_STORE = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'pdf')

運行與保存

  • 運行:scrapy crawl xiaoshuo
  • 保存
    • scrapy crawl xiaoshuo -o xiaoshuo.csv
    • scrapy crawl xiaoshuo-o xiaoshuo.xml
    • scrapy crawl maoyan -o maoyan.json -s FEED_EXPORT_ENCODING=UTF8

pipline自定義存儲格式

class TutoriaPipeline(object):
    def __init__(self):
        store_file = os.path.dirname(__file__) + 'xiaoshuo.csv'
        # print("store_file:", store_file)
        self.file = open(store_file, 'w', newline='')
        self.writer = csv.writer(self.file)

    def process_item(self, item, spider):
        # print(item['book_class'])
        # if item['book_class'] == '':
            # print("****************************")
        if item['book_class'] != "" or item['book_class'] != '':
            self.writer.writerow((item['book_title'], item['href_link'],  item['total_words'], item['brief_introduction']))
        return item
    def close_spider(self,spider):
        self.file.close()

Dowmloader Middleware的使用

  • 比如修改User-Agent,使用代理ip等。

數據流

  • ScrapyEngine打開一個網站,找到處理該網站的Spider,並向該Spider請求第一個(批)要爬取的url(s);
  • ScrapyEngine向調度器請求第一個要爬取的url,並加入到Schedule作爲請求以備調度;
  • ScrapyEngine向調度器請求下一個要爬取的url;
  • Schedule返回下一個要爬取的url給ScrapyEngine,ScrapyEngine通過DownloaderMiddlewares將url轉發給Downloader;
  • 頁面下載完畢,Downloader生成一個頁面的Response,通過DownloaderMiddlewares發送給ScrapyEngine;
  • ScrapyEngine從Downloader中接收到Response,通過SpiderMiddlewares發送給Spider處理;
  • Spider處理Response並返回提取到的Item以及新的Request給ScrapyEngine;
  • ScrapyEngine將Spider返回的Item交給ItemPipeline,將Spider返回的Request交給Schedule進行從第二步開始的重複操作,直到調度器中沒有待處理的Request,ScrapyEngine關閉。

解釋

Spiders:爬蟲,定義了爬取的邏輯和網頁內容的解析規則,主要負責解析響應並生成結果和新的請求
Engine:引擎,處理整個系統的數據流處理,出發事物,框架的核心。
Scheduler:調度器,接受引擎發過來的請求,並將其加入隊列中,在引擎再次請求時將請求提供給引擎
Downloader:下載器,下載網頁內容,並將下載內容返回給spider
ItemPipeline:項目管道,負責處理spider從網頁中抽取的數據,主要是負責清洗,驗證和向數據庫中存儲數據
Downloader Middlewares:下載中間件,是處於Scrapy的Request和Requesponse之間的處理模塊
Spider Middlewares:spider中間件,位於引擎和spider之間的框架,主要處理spider輸入的響應和輸出的結果及新的請求middlewares.py裏實現

常見問題

DNS lookup failed:no results for hostname lookup

修改setting.py文件,將ROBOTSTXT_OBEY = True改爲False: 拒絕遵守 Robot協議

請求網頁時傳遞參數

把需要傳遞的信息賦值給這個叫meta的變量,但meta只接受字典類型的賦值,因此要把待傳遞的信息改成“字典”的形式,即:
meta={‘key1’:value1,‘key2’:value2}
如果想在下一個函數中取出value1,只需得到上一個函數的meta[‘key1’]即可,因爲meta是隨着Request產生時傳遞的,下一個函數得到的Response對象中就會有meta,即response.meta,取value1則是value1=response.meta[‘key1’]

文件下載&存儲

# item.py
# FilesPipeline處理需要
# 當FilesPipeline處理時,會檢測是否有file_urls字段,如果有的話,會將url傳送給scarpy調度器和下載器;
# 下載完成之後,會將結果寫入item的另一字段files,files包含了文件現在的本地路徑(相對於配置FILE_STORE的路徑)、文件校驗和checksum、文件的url。
file_urls = scrapy.Field()
file = scrapy.Field()


# pipline.py
class JuchaoPipeline(FilesPipeline):  # 繼承自FilesPipeline

    def get_media_requests(self, item, info):
        yield scrapy.Request(item['file_urls'], meta = {'secCode':item['secCode'], 'announcementTitle':item['announcementTitle']})

    # 修改文件名
    def file_path(self, request, response=None, info=None):
        # print(response)
        secCode = request.meta['secCode']
        announcementTitle = request.meta['announcementTitle']
        # print(secCode, announcementTitle)
        # input()
        return '%s_%s.pdf' % (secCode, announcementTitle)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章