擼主:
Hekko 豈安科技研發工程師
喜歡(擅長)瞎折騰html
★
在這個言必稱「大數據」「人工智能」的時代,數據分析與挖掘逐漸成爲互聯網從業者必備的技能。本文介紹了利用輕量級爬蟲框架 scrapy 來進行數據採集的基本方法。
★mysql
scrapy 是一套用 Python 編寫的異步爬蟲框架,基於 Twisted 實現,運行於 Linux/Windows/MacOS 等多種環境,具備速度快、擴展性強、使用簡便等特色。即使是新手也能迅速掌握並編寫出所須要的爬蟲程序。scrapy 能夠在本地運行,也能部署到雲端(scrapyd)實現真正的生產級數據採集系統。sql
咱們經過一個實例來學習如何利用 scrapy 從網絡上採集數據。「博客園」是一個技術類的綜合資訊網站,本次咱們的任務是採集該網站 MySQL 類別 https://www.cnblogs.com/cate/... 下全部文章的標題、摘要、發佈日期、閱讀數量,共4個字段。最終的成果是一個包含了全部4個字段的文本文件。如圖所示:shell
最終拿到的數據以下所示,每條記錄有四行,分別是標題、閱讀數量、發佈時間、文章摘要:數據庫
下面來看看怎麼安裝 scrapy。首先你的系統裏必須得有 Python 和 pip,本文以最多見的 Python2.7.5 版本爲例。pip 是 Python 的包管理工具,通常來講 Linux 系統中都會默認安裝。在命令行下輸入以下命令並執行:瀏覽器
sudo pip install scrapy -i http://pypi.douban.com/simple –trusted-host=pypi.douban.com網絡
pip 會從豆瓣網的軟件源下載並安裝 scrapy,全部依賴的包都會被自動下載安裝。」sudo」的意思是以超級用戶的權限執行這條命令。全部的進度條都走完以後,若是提示相似」Successfully installed Twisted, scrapy … 「,則說明安裝成功。app
scrapy 同時也提供了一個可交互運行的 Shell,可以供咱們方便地測試解析規則。scrapy 安裝成功以後,在命令行輸入 scrapy shell 便可啓動 scrapy 的交互環境。scrapy shell 的提示符是三個大於號>>>,表示能夠接收命令了。咱們先用 fetch() 方法來獲取首頁內容:框架
>>> fetch( 「https://www.cnblogs.com/cate/mysql/」 )
若是屏幕上有以下輸出,則說明網頁內容已經獲取到了。異步
2017-09-04 07:46:55 [scrapy.core.engine] INFO: Spider opened 2017-09-04 07:46:55 [scrapy.core.engine] DEBUG: Crawled (200) <GET https://www.cnblogs.com/cate/mysql/> (referer: None)
獲取到的響應會保存在 response 對象中。該對象的 status 屬性表示 HTTP 響應狀態,正常狀況爲 200。
>>> print response.status 200
text 屬性表示返回的內容數據,從這些數據中能夠解析出須要的內容。
>>> print response.text u'<!DOCTYPE html>\r\n<html lang=」zh-cn」>\r\n<head>\r\n <meta charset=」utf-8″ />\r\n <meta name=」viewport」 content=」width=device-width, initial-scale=1″ />\r\n <meta name=」referrer」 content=」always」 />\r\n <title>MySQL – \u7f51\u7ad9\u5206\u7c7b – \u535a\u5ba2\u56ed</title>\r\n <link rel=」shortcut icon」 href=」//common.cnblogs.com/favicon.ico」 type=」image/x-icon」 />’
能夠看到是一堆很亂的 HTML 代碼,無法直觀地找到咱們須要的數據。這個時候咱們能夠經過瀏覽器的「開發者工具」來獲取指定數據的 DOM 路徑。用瀏覽器打開網頁 https://www.cnblogs.com/cate/... 以後,按下 F12 鍵便可啓動開發者工具,並迅速定位指定的內容。
能夠看到咱們須要的4個字段都在 / body / div(id=」wrapper」) / div(id=」main」) / div(id=」post_list」) / div(class=」post_item」) / div(class=」post_item_body」) / 下,每個」post_item_body」都包含一篇文章的標題、摘要、發佈日期、閱讀數量。咱們先獲取全部的」post_item_body」,而後再從裏面分別解析出每篇文章的4個字段。
>>> post_item_body = response.xpath( 「//div[@id=’wrapper’]/div[@id=’main’]/div[@id=’post_list’]/div[@class=’post_item’]/div[@class=’post_item_body’]」 ) >>> len( post_item_body ) 20
response 的 xpath 方法可以利用 xpath 解析器獲取 DOM 數據,xpath 的語法請參考官網文檔。能夠看到咱們拿到了首頁全部 20 篇文章的 post_item_body。那麼如何將每篇文章的這4個字段提取出來呢?
咱們以第一篇文章爲例。先取第一個 post_item_body:
>>> first_article = post_item_body[ 0 ]
標題在 post_item_body 節點下的 h3 / a 中,xpath 方法中text()的做用是取當前節點的文字,extract_first() 和 strip() 則是將 xpath 表達式中的節點提取出來並過濾掉先後的空格和回車符:
>>> article_title = first_article.xpath( 「h3/a/text()」 ).extract_first().strip() >>> print article_title
Mysql之表的操做與索引操做
而後用相似的方式提取出文章摘要:
>>> article_summary = first_article.xpath( 「p[@class=’post_item_summary’]/text()」 ).extract_first().strip() >>> print article_summary
表的操做: 1.表的建立: create table if not exists table_name(字段定義); 例子: create table if not exists user(id int auto_increment, uname varchar(20), address varch …
在提取 post_item_foot 的時候,發現提取出了兩組內容,第一組是空內容,第二組纔是「發佈於 XXX」的文字。咱們將第二組內容提取出來,並過濾掉「發佈於」三個字:
>>> post_date = first_article.xpath( 「div[@class=’post_item_foot’]/text()」 ).extract()[ 1 ].split( 「發佈於」 )[ 1 ].strip() >>> print post_date 2017-09-03 18:13
最後將閱讀數量提取出來:
>>> article_view = first_article.xpath( 「div[@class=’post_item_foot’]/span[@class=’article_view’]/a/text()」 ).extract_first() >>> print article_view 閱讀(6)
不少人以爲 xpath 方法裏的規則太過複雜。其實只要瞭解一點 HTML 文件的 DOM 結構,掌握 xpath 的提取規則仍是比較輕鬆容易的。好在 scrapy shell 容許咱們反覆對 DOM 文件進行嘗試解析。實驗成功的 xpath 表達式就能夠直接用在項目裏了。
scrapy shell 僅僅適用於測試目標網站是否能夠正常採集以及採集以後如何解析,真正作項目的時候還須要從頭創建一個 scrapy 項目。 輸入如下命令退出 scrapy shell 並返回 Linux 命令行:
>>> exit()
假設咱們的項目名稱叫 cnblogs_scrapy ,則可經過下面的命令來建立一個 scrapy 項目:
scrapy startproject cnblogs_scrapy
會自動生成以下結構的目錄與文件:
|– cnblogs_scrapy | |– __init__.py | |– items.py | |– middlewares.py | |– pipelines.py | |– settings.py | `– spiders | `– __init__.py `– scrapy.cfg
咱們須要改三個地方:
內容以下:
# -*- coding: utf-8 -*- import scrapy import sys reload( sys ) sys.setdefaultencoding( "utf8" ) class CnblogsMySQL(scrapy.Spider): # 爬蟲的名字,必須有這個變量 name = 'cnblogs_mysql' page_index = 1 # 初始地址,必須有這個變量 start_urls = [ 'https://www.cnblogs.com/cate/mysql/' + str( page_index ), ] def parse(self, response): post_items = response.xpath( "//div[@id='wrapper']/div[@id='main']/div[@id='post_list']/div[@class='post_item']/div[@class='post_item_body']" ) for post_item_body in post_items: yield { 'article_title': post_item_body.xpath( "h3/a/text()" ).extract_first().strip(), 'article_summary': post_item_body.xpath( "p[@class='post_item_summary']/text()" ).extract_first().strip(), 'post_date': post_item_body.xpath( "div[@class='post_item_foot']/text()" ).extract()[ 1 ].strip(), 'article_view' : post_item_body.xpath( "div[@class='post_item_foot']/span[@class='article_view']/a/text()" ).extract_first().strip() } next_page_url = None self.page_index += 1 if self.page_index <= 20: next_page_url = "https://www.cnblogs.com/cate/mysql/" + str( self.page_index ) else: next_page_url = None if next_page_url is not None: yield scrapy.Request(response.urljoin(next_page_url))
這個就是咱們的爬蟲,其中 name 和 start_urls 兩個變量必須存在。parse 方法的做用是將響應內容解析爲咱們須要的數據。parse 中的 for 循環就是在提取每一頁中的 20 篇文章。解析並提取完成後,經過 yield 將結果拋到 pipeline 進行存儲。
# -*- coding: utf-8 -*- # Define your item pipelines here # # Don't forget to add your pipeline to the ITEM_PIPELINES setting # See: http://doc.scrapy.org/en/latest/topics/item-pipeline.html class CnblogsScrapyPipeline(object): def open_spider( self, spider ): self.fp = open( "data.list", "w" ) def close_spider( self, spider ): self.fp.close() def process_item(self, item, spider): self.fp.write( item[ "article_title" ] + "\n" ) self.fp.write( item[ "article_view" ] + "\n" ) self.fp.write( item[ "post_date" ] + "\n" ) self.fp.write( item[ "article_summary" ] + "\n\n" ) return item
能夠看到有三個方法。這三個方法是從基類中繼承而來。open_spider/close_spider 分別在爬蟲啓動和結束的時候執行,通常用做初始化及收尾。process_item 會在每一次 spider 解析出數據後 yield 的時候執行,用來處理解析的結果。上面這個 pipeline 的做用是將每一條記錄都存儲到文件中。固然也能夠經過 pipeline 將內容存儲到數據庫或其它地方。
注意僅僅有這個 pipeline 文件還不能工做,須要在配置文件中向 scrapy 聲明 pipeline。同目錄下有個 settings.py 文件,加入以下內容:
ITEM_PIPELINES = { 'cnblogs_scrapy.pipelines.CnblogsScrapyPipeline': 300, }
後面的數字是 pipeline 的權重,若是一個爬蟲有多個 pipeline,各個 pipeline 的執行順序由這個權重來決定。
修改完成並保存以後,退到 cnblogs_scrapy 的上層目錄,並輸入如下命令啓動爬蟲:
scrapy crawl cnblogs_mysql
全部通過處理的信息都會輸出到屏幕上。結束以後,當前目錄中會生成名爲 data.list 的文件,裏面存儲了本次採集的全部數據。
cnblogs_mysql.py 的 parse 方法中有個 next_page_url 變量,通常狀況下這個變量的內容應當是當前頁面的下一頁 URL,該 URL 固然也能夠經過解析頁面來獲取。得到下一頁的URL以後,用 scrapy.Request 來發起新一次的請求。 簡單起見本文經過直接拼接 URL 的形式來指定僅採集前 20 頁的數據。
用 scrapy 發請求以前,也能夠本身構造 Request,這樣就能假裝爲真實訪問來避免被封。通常狀況下有修改 User-Agent、隨機採集時間、隨機代理 IP 等方法。 scrapy 項目能夠直接運行,也能夠部署在雲端進行批量採集和監控。雲端部署須要用到 scrapyd,操做起來也很簡單,有須要的話可自行參考官網文檔。