Scrapyhtml
Scrapy,Python開發的一個快速、高層次的屏幕抓取和web抓取框架,用於抓取web站點並從頁面中提取結構化的數據。Scrapy用途普遍,能夠用於數據挖掘、監測和自動化測試。
Scrapy吸引人的地方在於它是一個框架,任何人均可以根據需求方便的修改。它也提供了多種類型爬蟲的基類,如BaseSpider、sitemap爬蟲等,最新版本又提供了web2.0爬蟲的支持。
Scrap,是碎片的意思,這個Python的爬蟲框架叫Scrapy。node
Scrapy主要包括瞭如下組件:python
引擎(Scrapy)
用來處理整個系統的數據流處理, 觸發事務(框架核心)web
調度器(Scheduler)
用來接受引擎發過來的請求, 壓入隊列中, 並在引擎再次請求的時候返回. 能夠想像成一個URL(抓取網頁的網址或者說是連接)的優先隊列, 由它來決定下一個要抓取的網址是什麼, 同時去除重複的網址正則表達式
下載器(Downloader)
用於下載網頁內容, 並將網頁內容返回給蜘蛛(Scrapy下載器是創建在twisted這個高效的異步模型上的)數組
爬蟲(Spiders)
爬蟲是主要幹活的, 用於從特定的網頁中提取本身須要的信息, 即所謂的實體(Item)。用戶也能夠從中提取出連接,讓Scrapy繼續抓取下一個頁面cookie
項目管道(Pipeline)
負責處理爬蟲從網頁中抽取的實體,主要的功能是持久化實體、驗證明體的有效性、清除不須要的信息。當頁面被爬蟲解析後,將被髮送到項目管道,並通過幾個特定的次序處理數據。併發
下載器中間件(Downloader Middlewares)
位於Scrapy引擎和下載器之間的框架,主要是處理Scrapy引擎與下載器之間的請求及響應。app
爬蟲中間件(Spider Middlewares)
介於Scrapy引擎和爬蟲之間的框架,主要工做是處理蜘蛛的響應輸入和請求輸出。框架
調度中間件(Scheduler Middewares)
介於Scrapy引擎和調度之間的中間件,從Scrapy引擎發送到調度的請求和響應。
Scrapy運行流程以下:
引擎把URL封裝成一個請求(Request)傳給下載器
下載器把資源下載下來,並封裝成應答包(Response)
爬蟲解析Response
解析出實體(Item),則交給實體管道進行進一步的處理
安裝教程
1 Linux 2 pip3 install scrapy 3 Windows 4 a. pip3 install wheel 5 b. 下載twisted http://www.lfd.uci.edu/~gohlke/pythonlibs/#twisted 6 c. 進入下載目錄,執行 pip3 install Twisted‑17.1.0‑cp35‑cp35m‑win_amd64.whl 7 d. pip3 install scrapy 8 e. 下載並安裝pywin32:https://sourceforge.net/projects/pywin32/files/
新建項目
1 scrapy startproject xxxxx xxxxx爲新建的項目名稱 2 scrapy genspider <name> <domain> 3 例如 4 scrapy genspider chouti chouti.com
操做項目
1 cd進入xxxxx的文件夾 2 3 scrapy crawl 爬蟲應用名稱
新建Scrapy的目錄內容
1 project_name/ 2 scrapy.cfg 3 project_name/ 4 __init__.py 5 items.py 6 pipelines.py 7 settings.py 8 spiders/ 9 __init__.py 10 project_name.py
下面解釋一下文件
scrapy.cfg 項目的主配置信息。(真正爬蟲相關的配置信息在settings.py文件中)
items.py 設置數據存儲模板,用於結構化數據。
pipelines 數據處理行爲,如:通常結構化的數據持久化
settings.py 配置文件,如:遞歸的層數、併發數,延遲下載等
選擇器的使用
1 from scrapy.selector import Selector, HtmlXPathSelector
選擇器規則
nodeName 選取此節點的全部節點 / 從根節點選取 // 從匹配選擇的當前節點選擇文檔中的節點,不考慮它們的位置 . 選擇當前節點 .. 選取當前節點的父節點 @ 選取屬性 * 匹配任何元素節點 @* 匹配任何屬性節點 Node() 匹配任何類型的節點
具體語法
1 //div[id="Test"] #查找id爲Test的標籤 2 //a[re:test(@herf, '/all/hot/\d+')] # 正則表達式查找a標籤 3 .//h3/text() # h3標籤的文本內容 4 .//div[@class='part1']//a[1]/@href # a標籤的href文本
extract_first() 返回一個字符串
extract() 返回一個數組
攜帶cookies
1 # 方法一 2 print(response.headers.getlist('Set-Cookie')) 3 # 解析以後的cookie 4 cookie_dict = {} 5 cookie_jar = CookieJar() 6 cookie_jar.extract_cookies(response, response.request) 7 for k, v in cookie_jar._cookies.items(): 8 for i, j in v.items(): 9 for m, n in j.items(): 10 self.cookie_dict[m] = n.value 11 print(self.cookie_dict) 12 13 # 方法二,在Request對象添加meta={'cookiejar': True} 14 yield Request(url=url, callback=self.login, meta={'cookiejar': True})
請求頭-可在下載中間件添加
1 from scrapy.downloadermiddlewares.useragent import UserAgentMiddleware 2 3 class MyAgent(UserAgentMiddleware): 4 def __init__(self,user_agent=''): 5 self.user_agent = user_agent 6 7 def process_request(self,request,spider): 8 request.headers.setdefault('Host','www.jianshu.com') 9 request.headers.setdefault('User-Agent','Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/53.0.2785.143 Chrome/53.0.2785.143 Safari/537.36') 10 11 配置文件走一下 12 DOWNLOADER_MIDDLEWARES = { 13 'scrapy.contrib.downloadermiddleware.useragent.UserAgentMiddleware' : None,#必需 ,禁用默認的middleware 14 'Test.middlewares.MyAgent': 543, 15 }
去重
1 DUPEFILTER_CLASS = 'scrapy.dupefilter.RFPDupeFilter' 2 DUPEFILTER_DEBUG = False 3 JOBDIR = "保存範文記錄的日誌路徑,如:/root/" # 最終路徑爲 /root/requests.seen
自定義去重
1 class RepeatUrl: 2 def __init__(self): 3 self.visited_url = set() 4 5 @classmethod 6 def from_settings(cls, settings): 7 """ 8 初始化時,調用 9 :param settings: 10 :return: 11 """ 12 return cls() 13 14 def request_seen(self, request): 15 """ 16 檢測當前請求是否已經被訪問過 17 :param request: 18 :return: True表示已經訪問過;False表示未訪問過 19 """ 20 if request.url in self.visited_url: 21 return True 22 self.visited_url.add(request.url) 23 return False 24 25 def open(self): 26 """ 27 開始爬去請求時,調用 28 :return: 29 """ 30 print('open replication') 31 32 def close(self, reason): 33 """ 34 結束爬蟲爬取時,調用 35 :param reason: 36 :return: 37 """ 38 print('close replication') 39 40 def log(self, request, spider): 41 """ 42 記錄日誌 43 :param request: 44 :param spider: 45 :return: 46 """ 47 print('repeat', request.url)
爬蟲中間件
1 class SpiderMiddleware(object): 2 3 def process_spider_input(self,response, spider): 4 """ 5 下載完成,執行,而後交給parse處理 6 :param response: 7 :param spider: 8 :return: 9 """ 10 pass 11 12 def process_spider_output(self,response, result, spider): 13 """ 14 spider處理完成,返回時調用 15 :param response: 16 :param result: 17 :param spider: 18 :return: 必須返回包含 Request 或 Item 對象的可迭代對象(iterable) 19 """ 20 return result 21 22 def process_spider_exception(self,response, exception, spider): 23 """ 24 異常調用 25 :param response: 26 :param exception: 27 :param spider: 28 :return: None,繼續交給後續中間件處理異常;含 Response 或 Item 的可迭代對象(iterable),交給調度器或pipeline 29 """ 30 return None 31 32 33 def process_start_requests(self,start_requests, spider): 34 """ 35 爬蟲啓動時調用 36 :param start_requests: 37 :param spider: 38 :return: 包含 Request 對象的可迭代對象 39 """ 40 return start_requests
1 class DownMiddleware1(object): 2 def process_request(self, request, spider): 3 """ 4 請求須要被下載時,通過全部下載器中間件的process_request調用 5 :param request: 6 :param spider: 7 :return: 8 None,繼續後續中間件去下載; 9 Response對象,中止process_request的執行,開始執行process_response 10 Request對象,中止中間件的執行,將Request從新調度器 11 raise IgnoreRequest異常,中止process_request的執行,開始執行process_exception 12 """ 13 pass 14 15 16 17 def process_response(self, request, response, spider): 18 """ 19 spider處理完成,返回時調用 20 :param response: 21 :param result: 22 :param spider: 23 :return: 24 Response 對象:轉交給其餘中間件process_response 25 Request 對象:中止中間件,request會被從新調度下載 26 raise IgnoreRequest 異常:調用Request.errback 27 """ 28 print('response1') 29 return response 30 31 def process_exception(self, request, exception, spider): 32 """ 33 當下載處理器(download handler)或 process_request() (下載中間件)拋出異常 34 :param response: 35 :param exception: 36 :param spider: 37 :return: 38 None:繼續交給後續中間件處理異常; 39 Response對象:中止後續process_exception方法 40 Request對象:中止中間件,request將會被從新調用下載 41 """ 42 return None
設置代理
1 from scrapy.contrib.downloadermiddleware.httpproxy import HttpProxyMiddleware 2 3 方式一:使用默認 4 os.environ 5 { 6 http_proxy:http://root:woshiniba@192.168.11.11:9999/ 7 https_proxy:http://192.168.11.11:9999/ 8 } 9 方式二:使用自定義下載中間件 10 11 def to_bytes(text, encoding=None, errors='strict'): 12 if isinstance(text, bytes): 13 return text 14 if not isinstance(text, six.string_types): 15 raise TypeError('to_bytes must receive a unicode, str or bytes ' 16 'object, got %s' % type(text).__name__) 17 if encoding is None: 18 encoding = 'utf-8' 19 return text.encode(encoding, errors) 20 21 class ProxyMiddleware(object): 22 def process_request(self, request, spider): 23 PROXIES = [ 24 {'ip_port': '111.11.228.75:80', 'user_pass': ''}, 25 {'ip_port': '120.198.243.22:80', 'user_pass': ''}, 26 {'ip_port': '111.8.60.9:8123', 'user_pass': ''}, 27 {'ip_port': '101.71.27.120:80', 'user_pass': ''}, 28 {'ip_port': '122.96.59.104:80', 'user_pass': ''}, 29 {'ip_port': '122.224.249.122:8088', 'user_pass': ''}, 30 ] 31 proxy = random.choice(PROXIES) 32 if proxy['user_pass'] is not None: 33 request.meta['proxy'] = to_bytes("http://%s" % proxy['ip_port']) 34 encoded_user_pass = base64.encodestring(to_bytes(proxy['user_pass'])) 35 request.headers['Proxy-Authorization'] = to_bytes('Basic ' + encoded_user_pass) 36 print "**************ProxyMiddleware have pass************" + proxy['ip_port'] 37 else: 38 print "**************ProxyMiddleware no pass************" + proxy['ip_port'] 39 request.meta['proxy'] = to_bytes("http://%s" % proxy['ip_port']) 40 41 配置文件添加 42 DOWNLOADER_MIDDLEWARES = { 43 'step8_king.middlewares.ProxyMiddleware': 500, 44 }
設置證書
1 Https訪問 2 Https訪問時有兩種狀況: 3 1. 要爬取網站使用的可信任證書(默認支持) 4 DOWNLOADER_HTTPCLIENTFACTORY = "scrapy.core.downloader.webclient.ScrapyHTTPClientFactory" 5 DOWNLOADER_CLIENTCONTEXTFACTORY = "scrapy.core.downloader.contextfactory.ScrapyClientContextFactory" 6 7 2. 要爬取網站使用的自定義證書 8 DOWNLOADER_HTTPCLIENTFACTORY = "scrapy.core.downloader.webclient.ScrapyHTTPClientFactory" 9 DOWNLOADER_CLIENTCONTEXTFACTORY = "step8_king.https.MySSLFactory" 10 11 # https.py 12 from scrapy.core.downloader.contextfactory import ScrapyClientContextFactory 13 from twisted.internet.ssl import (optionsForClientTLS, CertificateOptions, PrivateCertificate) 14 15 class MySSLFactory(ScrapyClientContextFactory): 16 def getCertificateOptions(self): 17 from OpenSSL import crypto 18 v1 = crypto.load_privatekey(crypto.FILETYPE_PEM, open('/Users/wupeiqi/client.key.unsecure', mode='r').read()) 19 v2 = crypto.load_certificate(crypto.FILETYPE_PEM, open('/Users/wupeiqi/client.pem', mode='r').read()) 20 return CertificateOptions( 21 privateKey=v1, # pKey對象 22 certificate=v2, # X509對象 23 verify=False, 24 method=getattr(self, 'method', getattr(self, '_ssl_method', None)) 25 )
配置文件詳解
1 BOT_NAME = 'news' # 爬蟲的名字 會放在USER_AGENT中攜帶到請求裏 2 3 SPIDER_MODULES = ['news.spiders'] # 爬蟲的路徑 4 NEWSPIDER_MODULE = 'news.spiders' # 爬蟲的路徑 5 6 # USER_AGENT = 'news (+http://www.yourdomain.com)' # 同http請求的請求頭中User-Agent 7 USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 UBrowser/6.2.3964.2 Safari/537.36' 8 9 ROBOTSTXT_OBEY = True # 是否遵照爬蟲的規則,若是遵照會檢測網站中配置文件中是否容許爬蟲 10 11 CONCURRENT_REQUESTS = 32 # 請求併發數,默認併發16個請求 12 13 DOWNLOAD_DELAY = 3 # 下載延遲,表示每一個請求訪問前的阻塞3秒 14 CONCURRENT_REQUESTS_PER_DOMAIN = 16 # 針對每一個域名併發請求數 15 CONCURRENT_REQUESTS_PER_IP = 16 # 針對每一個IP的併發請求數 16 17 COOKIES_ENABLED = True # 是否爬取cookie, 默認爲True 18 COOKIES_DEBUG = True # 開啓cookie的debug模式,每次訪問都會打印cookie 19 20 21 TELNETCONSOLE_ENABLED = False # 監聽6023端口,能夠遠程查看爬蟲的狀況"telnet 127.0.0.1" 22 23 DEFAULT_REQUEST_HEADERS = { # http請求的請求頭 24 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', 25 'Accept-Language': 'zh-CN,zh;q=0.8', 26 } 27 28 SPIDER_MIDDLEWARES = { 29 'news.middlewares.NewsSpiderMiddleware': 543, 30 } 31 32 EXTENSIONS = { # 爬蟲的信號註冊到此選項 33 # 'scrapy.extensions.telnet.TelnetConsole': None, 34 'news.extensions.MyExtensions':300 35 } 36 37 ITEM_PIPELINES = { # 爬蟲的pipline註冊到此選項 38 'news.pipelines.NewsPipeline': 300, 39 } 40 41 SCHEDULER = 'scrapy.core.scheduler.Scheduler' # 調度器隊列 42 43 DEPTH_LIMIT = 1 # 爬蟲爬取深度 44 45 DEPTH_PRIORITY = 0 # 1:廣度優先,0:深度優先 46 47 DUPEFILTER_CLASS = 'news.duplicatioe.MyDupeFilter' # url重複過濾器
我的總結