上週學習了BeautifulSoup的基礎知識並用它完成了一個網路爬蟲( 使用Beautiful Soup編寫一個爬蟲 系列隨筆彙總),
BeautifulSoup是一個非常流行的Python網路抓取庫,它提供了一個基於HTML結構的Python物件。
雖然簡單易懂,又能非常好的處理HTML資料,
但是相比Scrapy而言,BeautifulSoup有一個最大的缺點:慢。
Scrapy 是一個開源的 Python 資料抓取框架,速度快,強大,而且使用簡單。
來看一個官網主頁上的簡單並完整的爬蟲:
雖然只有10行左右的程式碼,但是它的確是一個完整的爬蟲服務:
- 當執行scrapy runspider xxx.py命令的時候, Scrapy在專案裡查詢Spider(蜘蛛?️)並通過爬蟲引擎來執行它。
- 首先從定義在start_urls裡的URL開始發起請求,然後通過parse()方法處理響應。response引數就是返回的響應物件。
- 在parse()方法中,通過一個CSS選擇器獲取想要抓取的資料。
Scrapy所有的請求都是非同步的:
- 也就是說Scrapy不需要等一個請求完成才能處理下一條請求,而是同時發起另一條請求。
- 而且,非同步請求的另一個好處是當某個請求失敗了,其他的請求不會受到影響。
安裝(Mac)
1 |
pip install scrapy |
其他作業系統請參考完整安裝指導:
http://doc.scrapy.org/en/latest/intro/install.html
Scrapy中幾個需要了解的概念
Spiders
Spider類想要表達的是:如何抓取一個確定了的網站的資料。比如在start_urls裡定義的去哪個連結抓取,parse()方法中定義的要抓取什麼樣的資料。
當一個Spider開始執行的時候,它首先從start_urls()中的第一個連結開始發起請求,然後在callback裡處理返回的資料。
Items
Item類提供格式化的資料,可以理解為資料Model類。
Selectors
Scrapy的Selector類基於lxml庫,提供HTML或XML轉換功能。以response物件作為引數生成的Selector例項即可通過例項物件的xpath()方法獲取節點的資料。
編寫一個Web爬蟲
接下來將上一個Beautiful Soup版的抓取書籍資訊的例子( 使用Beautiful Soup編寫一個爬蟲 系列隨筆彙總)改寫成Scrapy版本。
新建專案
1 |
scrapy startproject book_project |
這行命令會建立一個名為book_project的專案。
編寫Item類
即實體類,程式碼如下:
1 2 3 4 5 6 |
import scrapy class BookItem(scrapy.Item): title = scrapy.Field() isbn = scrapy.Field() price = scrapy.Field() |
編寫Spider類
設定這個Spider的名稱,允許爬取的域名和從哪個連結開始:
1 2 3 4 5 6 |
class BookInfoSpider(scrapy.Spider): name = "bookinfo" allowed_domains = ["allitebooks.com", "amazon.com"] start_urls = [ "http://www.allitebooks.com/security/", ] |
遍歷分頁資料
1 2 3 4 5 6 |
def parse(self, response): # response.xpath('//a[contains(@title, "Last Page →")]/@href').re(r'(\d+)')[0] num_pages = int(response.xpath('//a[contains(@title, "Last Page →")]/text()').extract_first()) base_url = "http://www.allitebooks.com/security/page/{0}/" for page in range(1, num_pages): yield scrapy.Request(base_url.format(page), dont_filter=True, callback=self.parse_page) |
從allitebooks.com獲取書籍資訊方法
1 2 3 4 5 6 7 8 9 10 11 12 13 |
def parse_page(self, response): for sel in response.xpath('//div/article'): book_detail_url = sel.xpath('div/header/h2/a/@href').extract_first() yield scrapy.Request(book_detail_url, callback=self.parse_book_info) def parse_book_info(self, response): title = response.css('.single-title').xpath('text()').extract_first() isbn = response.xpath('//dd[2]/text()').extract_first() item = BookItem() item['title'] = title item['isbn'] = isbn amazon_search_url = 'https://www.amazon.com/s/ref=nb_sb_noss?url=search-alias%3Daps&field-keywords=' + isbn yield scrapy.Request(amazon_search_url, callback=self.parse_price, meta={ 'item': item }) |
‘//a’的意思所有的a標籤;
‘//a[contains(@title, “Last Page →”)’ 的意思是在所有的a標籤中,title屬性包涵”Last Page →”的a標籤;
extract() 方法解析並返回符合條件的節點資料。
從amazon.com獲取書籍價格方法
1 2 3 4 |
def parse_price(self, response): item = response.meta['item'] item['price'] = response.xpath('//span/text()').re(r'\$[0-9]+\.[0-9]{2}?')[0] yield item |
啟動服務
1 |
scrapy crawl bookinfo -o books.csv |
-o books.csv 引數的意思是將抓取的Item集合輸出到csv檔案。
除了CSV格式,Scrapy還支援JSON,XML的格式輸入。具體請參考:
http://doc.scrapy.org/en/latest/topics/feed-exports.html#topics-feed-exports
結果:
完整程式碼請移步GitHub:
https://github.com/backslash112/book_scraper_scrapy
我們處於大資料時代,對資料處理感興趣的朋友歡迎檢視另一個系列隨筆:
利用Python進行資料分析 基礎系列隨筆彙總