Date: 2019-07-15html
Author: Sunpython
Scrapy是一個爲了爬取網站數據、提取結構化數據而編寫的爬蟲應用框架。Scrapy內部實現了包括併發請求、免登陸、URL去重等不少複雜操做,用戶不須要明白Scrapy內部具體的爬取策略,只須要根據本身的須要,編寫小部分的代碼,就能抓取到所須要的數據mysql
此節咱們學習下如何採用採用scrapy進行項目流程開發和配置web
使用startproject命令建立項目sql
scrapy startproject scrapy_proj #使用scrapy產生一個scrapy_name爬蟲項目
使用genspider命令在項目中建立爬蟲腳本shell
cd scrapy_proj/scrapy_proj scrapy genspider --list scrapy genspider myspider "www.myspider_domain.com"
此時會在scrapy_proj/spiders/產生一個新的文件myspider.py數據庫
這個是咱們的爬取頁面的主入口和頁面下載完成後解析主入口。json
設置settings.py 文件,設置相關的配置信息,具體配置見下面參數的說明api
配置文件參數說明瀏覽器
(1)ROBOTSTXT_OBEY = True ————— 是否遵照robots.txt規則
說明:
robots.txt 是遵循 Robot協議 的一個文件,它保存在網站的服務器中,它的做用是,告訴搜索引擎爬蟲,本網站哪些目錄下的網頁 不但願 你進行爬取收錄。在Scrapy啓動後,會在第一時間訪問網站的 robots.txt 文件,而後決定該網站的爬取範圍。(在某些狀況下咱們想要獲取的內容偏偏是被 robots.txt 所禁止訪問的。因此,某些時候,咱們就要將此配置項設置爲 False ,拒絕遵照 Robot協議 !)
(2)CONCURRENT_REQUESTS = 16-----------開啓線程數量,默認16,能夠自行設置
這個參數涉及到scrapy爬取的併發量,items的處理速度
(3)DOWNLOAD_DELAY = 3 ——— 下載延遲時間。下載器在下載同一個網站下一個頁面前須要等待的時間。該選項能夠用來限制爬取速度, 減輕服務器壓力。(反爬策略之一)
(4)CONCURRENT_REQUESTS_PER_DOMAIN = 16 將對任何單個域執行的併發(即同時)請求的最大數量。
CONCURRENT_REQUESTS_PER_IP = 16 將對任何單個IP執行的併發(即同時)請求的最大數量。若是非零,CONCURRENT_REQUESTS_PER_DOMAIN則忽略該 設置,而改成使用此設置。換句話說,併發限制將應用於每一個IP,而不是每一個域。
(5)COOKIES_ENABLED = False
是否啓用cookie。是否啓用cookies middleware。若是關閉,cookies將不會發送給web server。
除非您真的 須要,不然請禁止cookies。在進行通用爬取時cookies並不須要, (搜索引擎則忽略cookies)。禁止cookies能減小CPU使用率及Scrapy爬蟲在內存中記錄的蹤影,提升性能。
COOKIES_DEBUG:默認: False
若是啓用,Scrapy將記錄全部在request(cookie 請求頭)發送的cookies及response接收到的cookies(set-cookie接收頭)
(6)AUTOTHROTTLE_START_DELAY = 5
初始下載延遲時間(單位:秒)
(7)AUTOTHROTTLE_MAX_DELAY = 60
高併發請求時最大延遲時間(單位:秒)
(8) USER_AGENT 用戶代理
這個是相當重要的,大部分服務器在請求快了會首先檢查User_Agent,而scrapy默認的瀏覽器頭是scrapy1.1 咱們須要開啓而且修改爲瀏覽器頭,如:Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/22.0.1207.1 Safari/537.1。 可是最好是這個USER-AGENT會隨機自動更換最好了。
(8)DEFAULT_REQUEST_HEADERS
默認請求頭部信息,例如以下配置
DEFAULT_REQUEST_HEADERS = { 'accept': 'image/webp,*/*;q=0.8', 'accept-language': 'zh-CN,zh;q=0.8', 'referer': 'https://www.taobao.com/', 'user-agent': 'Mozilla/5.0 (Windows NT 6.3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.63 Safari/537.36', }
這個是瀏覽器請求頭,不少網站都會檢查客戶端的headers,好比豆瓣就是每個請求都檢查headers的user_agent,不然只會返回403,能夠開啓user-agent
(9) SPIDER_MIDDLEWARES
Spider中間件是介入到Scrapy中的spider處理機制的鉤子框架,能夠插入自定義功能來處理髮送給 Spiders 的response,以及spider產生的item和request。
要啓用Spider中間件(Spider Middlewares),能夠將其加入到 SPIDER_MIDDLEWARES 設置中。 該設置是一個字典,鍵爲中間件的路徑,值爲中間件的順序(order)。
(10) DOWNLOADER_MIDDLEWARES
要激活下載器中間件組件,將其加入到 DOWNLOADER_MIDDLEWARES 設置中。 該設置是一個字典(dict),鍵爲中間件類的路徑,值爲其中間件的順序(order)。
(11)ITEM_PIPELINES
每一個Item Pipeline組件其實就是一個實現了一個簡單方法的Python類。他們接受一個item並在上面執行邏輯,還能決定這個item究竟是否還要繼續往下傳輸,若是不要了就直接丟棄。
(12)AUTOTHROTTLE — 自動限速 (反爬策略之一)
AUTOTHROTTLE_ENABLED = True #初始下載延遲 # The initial download delay AUTOTHROTTLE_START_DELAY = 5 #在高延遲的狀況下設置的最大下載延遲 # The maximum download delay to be set in case of high latencies AUTOTHROTTLE_MAX_DELAY = 60 #Scrapy請求的平均數量應該並行發送每一個遠程服務器 # The average number of requests Scrapy should be sending in parallel to # each remote server AUTOTHROTTLE_TARGET_CONCURRENCY = 1.0 # Enable showing throttling stats for every response received: AUTOTHROTTLE_DEBUG = False
(13)是否啓用在本地緩存,若是開啓會優先讀取本地緩存,從而加快爬取速度,視狀況而定
HTTPCACHE_ENABLED = True
HTTPCACHE_EXPIRATION_SECS = 0
HTTPCACHE_DIR = 'httpcache'
HTTPCACHE_IGNORE_HTTP_CODES = []
HTTPCACHE_STORAGE = 'scrapy.extensions.httpcache.FilesystemCacheStorage'
答案:在settings.py中加入以下信息
LOG_LEVEL= 'INFO' #日誌級別 LOG_FILE ='log.txt' #日誌打印的文件名稱
DEBUG < INFO < WARNING < ERROR
日誌案例:(settings.py中設置以下信息)
############### log settings begin ###################### LOG_LEVEL = "INFO" from datetime import datetime import os today = datetime.now() LOG_DIR = "logs" if not os.path.exists(LOG_DIR): os.mkdir(LOG_DIR) LOG_FILE = "{}/scrapy_{}_{}_{}.log".format(LOG_DIR, today.year, today.month, today.day) ############### log settings end ######################
糗事百科的主頁:www.qiushibaike.com
使用startproject命令建立項目(糗事百科爬蟲項目)
scrapy startproject qiubai_proj #使用scrapy產生一個scrapy_name爬蟲項目
使用genspider命令在項目中建立爬蟲腳本
cd qiubai_proj/qiubai_proj scrapy genspider qiubai "www.qiushibaike.com"
此時會在qiubai_proj/spiders/產生一個新的文件qiubai.py
這個是咱們的爬取頁面的主入口和頁面下載完成後解析主入口。
修改配置文件settings.py:
BOT_NAME = 'qiubai_proj' SPIDER_MODULES = ['qiubai_proj.spiders'] NEWSPIDER_MODULE = 'qiubai_proj.spiders' # Crawl responsibly by identifying yourself (and your website) on the user-agent #USER_AGENT = 'qiubai_proj (+http://www.yourdomain.com)' USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) " \ "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36" # Obey robots.txt rules #ROBOTSTXT_OBEY = True ROBOTSTXT_OBEY = False LOG_LEVEL= 'DEBUG'
分析糗事百科網站,定義數據模型用於保存爬取的數據。
編輯文件qiubai_proj/qiubai_proj/items.py, 內容以下:
# -*- coding: utf-8 -*- # Define here the models for your scraped items # # See documentation in: # https://doc.scrapy.org/en/latest/topics/items.html import scrapy class QiubaiProjItem(scrapy.Item): # define the fields for your item here like: #保存頭像連接 image_url = scrapy.Field() #保存名字 name = scrapy.Field() #保存年齡 age = scrapy.Field() #保存內容 content = scrapy.Field() #可笑的個數 haha_count = scrapy.Field()
關於xpath語法使用請參考day03中的xpath語法章節。
# -*- coding: utf-8 -*- import scrapy class QiubaiSpider(scrapy.Spider): name = 'qiubai' allowed_domains = ['www.qiushibaike.com'] start_urls = ['http://www.qiushibaike.com/'] #主入口url #parse函數就是文件解析函數,response就是響應對象 def parse(self, response): # with open("qiubai.html", 'w', encoding="utf-8") as f: # f.write(response.text) div_list = response.xpath('//div[starts-with(@id, "qiushi_tag_")]') print(type(div_list)) #遍歷列表,獲取列表內容 item_list = [] for div in div_list: ''' 先經過xpath獲取內容,返回的是一個列表 而後經過extract()轉換成unicode字符串,再獲取第0個, 也就是指定的內容 將解析到的內容保存到字典中 ''' image_url = div.xpath('./div[@class="author clearfix"]//img/@src').extract_first() name = div.xpath('./div[@class="author clearfix"]//h2/text()').extract_first() age = div.xpath('./div[@class="author clearfix"]/div/text()').extract_first() content = div.xpath('./a/div[@class="content"]/span/text()').extract() content = ' '.join(content) haha_count = div.xpath('./div[@class="stats"]/span[@class="stats-vote"]/i/text()').extract()[0] item = dict( image_url = image_url, name = name, age = age, content = content, haha_count = haha_count ) yield item #item_list.append(item) #return item_list
(1)保存爬取的內容到json文件中
scrapy crawl qiubai -o qiubai.json
能夠查看產生的json文件,將內容拷貝到json在線格式網站
https://www.json.cn/, 看數據爬取是否和真實相符。
(2)保存爬取的數據到xml文件中
scrapy crawl qiubai -o qiubai.xml
(3)保存爬取的數據到數據報表csv文件中
scrapy crawl qiubai -o qiubai.csv
知識總結:
經過指令建立爬蟲文件 cd qiubai_proj/qiubai_proj scrapy genspider qiubai "www.qiushibaike.com" 那麼就會在firstSpider/firstSpider/spiders裏面自動建立一個qiubai.py name: 爬蟲的名字,啓動的時候根據爬蟲的名字啓動項目 allowed_domains:容許的域名,就是爬取的時候這個請求要不要發送,若是是該容許域名之下的url,就會發送,若是不是,則過濾掉這個請求,這是一個列表,能夠寫多個容許的域名 start_urls:爬蟲起始url,是一個列表,裏面能夠寫多個,通常只寫一個 def parse(self, response): 這個函數很是重要,就是你之後寫代碼的地方,parse函數名是固定的,當收到下載數據的時候會自動的調用這個方法,該方法第二個參數爲response,這是一個響應對象,從該對象中獲取html字符串,而後解析之。
糗事百科的主頁:www.qiushibaike.com
本節咱們將對上節數據進行數據保存(持久化)
本節分別將數據保存到json文件和mysql數據庫中
使用 Scrapy 提供的 exporter 存儲 Json 數據
Scrapy 爲咱們提供了一個 JsonItemExporter 類來進行 Json 數據的存儲,很是方便
修改上節中的spiders
使用yield改造,使得spider成爲一個生成器,不斷地往pipeline裏面流入待處理的數據
# -*- coding: utf-8 -*- import scrapy class QiubaiSpider(scrapy.Spider): name = 'qiubai' allowed_domains = ['www.qiushibaike.com'] start_urls = ['http://www.qiushibaike.com/'] # 主入口url # parse函數就是文件解析函數,response就是響應對象 def parse(self, response): # with open("qiubai.html", 'w', encoding="utf-8") as f: # f.write(response.text) div_list = response.xpath('//div[starts-with(@id, "qiushi_tag_")]') #print(type(div_list)) # 遍歷列表,獲取列表內容 item_list = [] for div in div_list: ''' 先經過xpath獲取內容,返回的是一個列表 而後經過extract()轉換成unicode字符串,再獲取第0個, 也就是指定的內容 將解析到的內容保存到字典中 ''' image_url = div.xpath('./div[@class="author clearfix"]//img/@src').extract_first() name = div.xpath('./div[@class="author clearfix"]//h2/text()').extract_first().strip("\n") age = div.xpath('./div[@class="author clearfix"]/div/text()').extract_first() contents = div.xpath('./a/div[@class="content"]/span/text()').extract() content = ' '.join([ct.strip() for ct in contents]) haha_count = div.xpath('./div[@class="stats"]/span[@class="stats-vote"]/i/text()').extract()[0] item = dict( image_url=image_url, name=name, age=age, content=content, haha_count=haha_count ) yield item # item_list.append(item) # return item_list
在settings.py文件中開啓ITEM_PIPELINES選項,開啓以下信息
# Configure item pipelines # See https://doc.scrapy.org/en/latest/topics/item-pipeline.html ITEM_PIPELINES = { 'qiubai_proj.pipelines.QiubaiProjPipeline': 300, }
pipelines的邏輯處理
經過pipelines將spiders轉發過來的item數據進行導入到json報表中
編輯qiubai_proj/pipelines.py文件,添加以下內容
# -*- coding: utf-8 -*- # Define your item pipelines here # # Don't forget to add your pipeline to the ITEM_PIPELINES setting # See: https://doc.scrapy.org/en/latest/topics/item-pipeline.html from scrapy.exporters import JsonItemExporter class QiubaiProjPipeline(object): # 調用 scrapy 提供的 json exporter 導出 json 文件 def __init__(self): self.file = open('questions_exporter.json', 'wb') # 初始化 exporter 實例,執行輸出的文件和編碼 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() def process_item(self, item, spider): ''' 使用 scrapy.exporters.JsonItemExporter 生成的文件 ''' print("########### pipelines begin ###############") #print(item) self.exporter.export_item(item) print("########### pipelines end ###############") return item
上述pipeline將統計的item結果數據保存到questions_exporter.json中
添加日誌功能
settings.py中添加日誌功能 文件及路徑,log目錄須要先建好
today = datetime.now()
log_file_path = "log/scrapy_{}{}{}.log".format(today.year, today.month, today.day)
日誌輸出
LOG_LEVEL = 'DEBUG'
LOG_FILE = log_file_path
scrapy crawl qiubai
運行成功後,能夠查看questions_exporter.json文件
依賴庫:pymysql
Twisted 是一個異步網絡框架,不幸的是大部分數據庫api實現只有阻塞式接口,twisted.enterprise.adbapi爲此產生,它是DB-API 2.0 API的非阻塞接口,能夠訪問各類關係數據庫。
不一樣於直接建立數據庫鏈接, 而是使用 adbapi.ConnectionPool 類來管理鏈接. 這就可讓 adbapi 來使用多個鏈接, 好比每一個線程一個鏈接,這很簡單:
使用前面例子的 "dbmodule" , 來建立一個 ConnectionPool 對象
from twisted.enterprise import adbapi鏈接mysql,要這樣:
_conn = adbapi.ConnectionPool(' db='test', user='root', passwd='aaaa', host='localhost',use_unicode=True, charset='utf8'*)
數據庫定義
建立數據庫
create database qiubai_db default charset=utf8; CREATE TABLE qiubai ( id int(11) PRIMARY KEY auto_increment COMMENT '設置主鍵自增', image_url varchar(150) NOT NULL COMMENT '圖片url連接', name VARCHAR(50) COMMENT '名稱', age VARCHAR(10) COMMENT '年齡', content VARCHAR(500) COMMENT '內容', haha_count INT COMMENT '笑點數' ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
將上述內容放入到table.sql文件,進入mysql終端中,執行 source ./table.sql;
建立數據庫和表成功!
settings.py文件中添加以下mysql數據庫配置信息:
MYSQL_SETTINGS = { 'HOST':'192.168.51.63', #數據庫地址 'DATABASE':'qiubai_proj', #數據庫名稱 'USER':'zhougy', #登錄用戶名 'PASSWORD':'123456', #登錄密碼 'CHARSET':'utf8', #字符編碼 }
添加文件 mysql_pipelines.py, 內容添加以下:
scrapy框架底層網絡庫是twisted,同時他也
此文件採用twisted.enterprise.adbapi異步處理接口處理mysql操做
# -*- coding: utf-8 -*- __author__ = 'zhougy' __date__ = '2018/9/2 下午7:26' import json import pymysql from twisted.enterprise import adbapi from .settings import MYSQL_SETTINGS import logging class MysqlTwistedPipline(object): def __init__(self, dbpool): self.dbpool = dbpool @classmethod def from_settings(cls, settings): dbparms = dict( host=MYSQL_SETTINGS['HOST'], db=MYSQL_SETTINGS['DATABASE'], user=MYSQL_SETTINGS['USER'], passwd=MYSQL_SETTINGS['PASSWORD'], charset=MYSQL_SETTINGS['CHARSET'], cursorclass=pymysql.cursors.DictCursor, # 指定 curosr 類型 use_unicode=True, ) # 指定擦作數據庫的模塊名和數據庫參數參數 dbpool = adbapi.ConnectionPool("pymysql", **dbparms) return cls(dbpool) def process_item(self, item, spider): ''' 使用twisted將mysql插入變成異步處理 :param item: :param spider: :return: ''' query = self.dbpool.runInteraction(self.db_insert, item) # 指定異常處理方法 query.addErrback(self.handle_error, item, spider) # 處理異常 return item def handle_error(self, failure, item, spider): # 處理異步插入的異常 print("######################1") #print(item) #print(failure) logging.error(f"handler_error has error failure: {failure}") logging.warning() print("########################2") def db_insert(self, cursor, item): # 執行具體的插入 # 根據不一樣的item 構建不一樣的sql語句並插入到mysql中 insert_sql, params = self.get_insert_sql(item) cursor.execute(insert_sql, params) logging.info(f"write db ok with data:{item['name']}") def get_insert_sql(self, item): insert_sql = """ insert into qiubai(image_url, name, age, content, haha_count) VALUES (%s, %s, %s, %s, %s) """ params = ( item["image_url"], item["name"], item["age"], item["content"], item["haha_count"]) return insert_sql, params
至此,基於mysql的pipeline異步處理管道邏輯就已經完成。
修改配置文件settings.py
將上述MysqlTwistedPipline管道處理類添加到配置文件中
ITEM_PIPELINES = { 'qiubai_proj.pipelines.QiubaiProjPipeline': 300, 'qiubai_proj.mysql_pipelines.MysqlTwistedPipline': 300, }
運行啓動spider
scrapy crawl qiubai
運行成功後,能夠查看數據庫表數據。