文章目錄
參考原文:
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)