一、前言
我們在使用爬蟲程式爬取網頁時,一般對於靜態頁面的爬取是比較簡單的,之前寫過挺多的案例。但是對於使用js動態載入的頁面如何爬取呢?
對於動態js頁面的爬取有以下幾種爬取的方式:
#透過selenium+phantomjs實作。
phantomjs是一個無頭瀏覽器,selenium是一個自動化測試的框架,透過無頭瀏覽器請求頁面,等待js加載,再透過自動化測試selenium取得數據。因為無頭瀏覽器非常消耗資源,所在效能方面有所欠缺。
Scrapy-splash框架:
#Splash作為js渲染服務,是基於Twisted和QT開發的輕量瀏覽器引擎,並提供直接的http api。快速、輕量的特點使其容易進行分散式開發。
splash和scrapy爬蟲框架融合,兩種互相相容的特點,抓取效率較好。
Splash服務是基於docker容器的,所以我們需要先安裝docker容器。
2.1 docker安裝(windows 10 家用版)
如果是win 10專業版或其他作業系統,都是比較好安裝的,在windows 10家用版安裝docker需要透過toolbox(需要最新的)工具安裝才行。
關於docker的安裝,請參考文件:WIN10安裝Docker
2.2 splash安裝
docker pull scrapinghub/splash
2.3 啟動Splash服務
docker run -p 8050:8050 scrapinghub/splash
這個時候,打開你的瀏覽器,輸入192.168.99.100:8050你會看到出現了這樣的介面。
你可以在上圖紅色框框的地方輸入任意的網址,點擊後面的Render me! 來查看渲染之後的樣子
2.4 安裝python的scrapy-splash包
pip install scrapy-splash
由於業務需要爬取一些國外的新聞網站,如google news。但是發現居然是js程式碼。於是開始使用scrapy-splash框架,配合Splash的js渲染服務,取得資料。具體看如下程式碼:
3.1 settings.py配置資訊
# 渲染服务的urlSPLASH_URL = 'http://192.168.99.100:8050'# 去重过滤器DUPEFILTER_CLASS = 'scrapy_splash.SplashAwareDupeFilter'# 使用Splash的Http缓存HTTPCACHE_STORAGE = 'scrapy_splash.SplashAwareFSCacheStorage'SPIDER_MIDDLEWARES = { 'scrapy_splash.SplashDeduplicateArgsMiddleware': 100, }#下载器中间件DOWNLOADER_MIDDLEWARES = { 'scrapy_splash.SplashCookiesMiddleware': 723, 'scrapy_splash.SplashMiddleware': 725, 'scrapy.downloadermiddlewares.httpcompression.HttpCompressionMiddleware': 810, }# 请求头DEFAULT_REQUEST_HEADERS = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.89 Safari/537.36', 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', }# 管道ITEM_PIPELINES = { 'news.pipelines.NewsPipeline': 300, }
3.2 items欄位定義
class NewsItem(scrapy.Item): # 标题 title = scrapy.Field() # 图片的url链接 Scrapy與scrapy-splash框架快速載入js頁面_url = scrapy.Field() # 新闻来源 source = scrapy.Field() # 点击的url action_url = scrapy.Field()
3.3 Spider程式碼
在spider目錄下,創建一個new_spider.py的文件,文件內容如下:
from scrapy import Spiderfrom scrapy_splash import SplashRequestfrom news.items import NewsItemclass GoolgeNewsSpider(Spider): name = "google_news" start_urls = ["https://news.google.com/news/headlines?ned=cn&gl=CN&hl=zh-CN"] def start_requests(self): for url in self.start_urls: # 通过SplashRequest请求等待1秒 yield SplashRequest(url, self.parse, args={'wait': 1}) def parse(self, response): for element in response.xpath('//p[@class="qx0yFc"]'): actionUrl = element.xpath('.//a[@class="nuEeue hzdq5d ME7ew"]/@href').extract_first() title = element.xpath('.//a[@class="nuEeue hzdq5d ME7ew"]/text()').extract_first() source = element.xpath('.//span[@class="IH8C7b Pc0Wt"]/text()').extract_first() Scrapy與scrapy-splash框架快速載入js頁面Url = element.xpath('.//img[@class="lmFAjc"]/@src').extract_first() item = NewsItem() item['title'] = title item['Scrapy與scrapy-splash框架快速載入js頁面_url'] = Scrapy與scrapy-splash框架快速載入js頁面Url item['action_url'] = actionUrl item['source'] = source yield item
3.4 pipelines.py代碼
將item的數據,儲存到mysql資料庫。
建立db_news資料庫
CREATE DATABASE db_news
建立tb_news表
CREATE TABLE tb_google_news( id INT AUTO_INCREMENT, title VARCHAR(50), Scrapy與scrapy-splash框架快速載入js頁面_url VARCHAR(200), action_url VARCHAR(200), source VARCHAR(30), PRIMARY KEY(id) )ENGINE=INNODB DEFAULT CHARSET=utf8;
class NewsPipeline(object): def __init__(self): self.conn = pymysql.connect(host='localhost', port=3306, user='root', passwd='root', db='db_news',charset='utf8') self.cursor = self.conn.cursor() def process_item(self, item, spider): sql = '''insert into tb_google_news (title,Scrapy與scrapy-splash框架快速載入js頁面_url,action_url,source) values(%s,%s,%s,%s)''' self.cursor.execute(sql, (item["title"], item["Scrapy與scrapy-splash框架快速載入js頁面_url"], item["action_url"], item["source"])) self.conn.commit() return item def close_spider(self): self.cursor.close() self.conn.close()