轉(zhuǎn)載自:https://zhuanlan.zhihu.com/p/130867872
本文概要
- 展示了一個(gè)使用Scrapy爬取網(wǎng)頁信息的例子——爬取天天基金網(wǎng)的貨幣基金
- 爬取的信息在JS渲染的動(dòng)態(tài)頁面中,使用headless瀏覽器和Splash配合Scrapy解決問題
- 提供了docker鏡像和Dockerfile挪略,詳述過程Dockerfile每一行的設(shè)置和其中的坑
- 仔細(xì)閱讀可以了解Scrapy的基本用法踏揣,配合headless瀏覽器爬取一切疑難頁面假勿,在2.0.1版本下很長一段時(shí)間不用去和環(huán)境死磕了岸霹,直接取用docker鏡像吧。
代碼
任務(wù)
由天天基金網(wǎng)的貨基列表頁進(jìn)入詳情頁
爬取詳情頁中一系列信息
Scrapy一般流程
Scrapy目前的版本是2.0.1贞奋,其官方文檔在https://scrapy.org/
需要python3.5及以上的環(huán)境窒篱,在linux下需要Ubuntu 14.04及以上。
使用pip和conda很容易安裝注益,pip install Scrapy
即可碴巾。
Scrapy提供了腳手架,scrapy startproject tutorial
創(chuàng)建項(xiàng)目丑搔,獲得如下的目錄:
tutorial/
scrapy.cfg # deploy configuration file
tutorial/ # project's Python module, you'll import your code from here
__init__.py
items.py # project items definition file
middlewares.py # project middlewares file
pipelines.py # project pipelines file
settings.py # project settings file
spiders/ # a directory where you'll later put your spiders
__init__.py
核心邏輯在Spiders目錄下厦瓢,可以增加多個(gè)python文件提揍,每一個(gè)都描述了一段抓取邏輯,Scrapy可以單獨(dú)其中其中一個(gè)爬蟲煮仇,也可以全部啟動(dòng)劳跃。
from scrapy.spiders import Spider
import scrapy
from scrapy_selenium import SeleniumRequest
class HJSpider(Spider):
name = 'hj_spider'
start_urls = ['http://fund.eastmoney.com/HBJJ_dwsy.html']
def parse(self, response):
if response.url == 'http://fund.eastmoney.com/HBJJ_dwsy.html':
urls = response.xpath('//*/td[5][@class="jc"]/nobr/a[1]/@href').extract()[0:4]
for url in urls:
yield scrapy.Request(url, callback=self.parse)
else:
self.parse_detail(response)
def parse_detail(self, response):
name = response.xpath('//*[@id="body"]/div[4]/div[9]/div/div/div[1]/div[1]/div/text()').extract()
nh_shouyi = response.xpath('//*[@id="body"]/div[4]/div[9]/div/div/div[2]/div[1]/div[1]/dl[4]/dd/span/text()').extract()
start_date = response.xpath('//*[@id="body"]/div[4]/div[9]/div/div/div[2]/div[1]/div[3]/table/tbody/tr[2]/td[1]/text()').extract()
scale = response.xpath('//*[@id="body"]/div[4]/div[9]/div/div/div[2]/div[1]/div[3]/table/tbody/tr[1]/td[2]/text()').extract()
ttt = response.xpath('//*[@id="highcharts-24"]/svg/g[5]/g[3]/rect[4]/@height').extract()
# print (response.url, name, nh_shouyi, start_date, scale, ttt)
print ('{};;{};;{};;{};;{};;{}'.format(name[0], response.url, nh_shouyi[0], start_date[0], scale[0], ttt[0]))
spider文件的要素:
- name屬性必須有,且項(xiàng)目內(nèi)唯一浙垫,
scrapy crawl spider_name
可以單獨(dú)執(zhí)行該爬蟲 - start_urls定義起始頁面的url的列表刨仑,爬蟲會(huì)由此遞歸運(yùn)行
- parse函數(shù)用來解析頁面返回,通過
yield
Request對象夹姥,可以遞歸地運(yùn)行下去
該例子中杉武,我們從基金列表頁的URL進(jìn)入,解析得到每只基金的詳情頁面辙售,進(jìn)入后爬取需要的信息轻抱。
從DOM樹上獲取信息,Scrapy提供了xpath和css兩種選擇器旦部,這里我使用了xpath祈搜,配合chrome瀏覽器,非常的輕松士八。
右擊需要獲取的信息容燕,點(diǎn)擊菜單中的選項(xiàng)。
然后就能看到頁面源碼對應(yīng)的位置婚度,右擊復(fù)制xpath缰趋,即可得到代碼中的路徑,自己稍作調(diào)整即可達(dá)到要求陕见。
使用命令Scrapy shell url
可以在交互的環(huán)境中調(diào)試秘血。
編寫完成后啟動(dòng)爬蟲 scrapy crawl hj_spider
爬取JS渲染信息
在爬取如下圖標(biāo)信息的時(shí)候,雖然在瀏覽器中可以在DOM樹上看到數(shù)據(jù)评甜,但實(shí)際在Scrapy shell
中調(diào)試灰粮,發(fā)現(xiàn)數(shù)據(jù)信息是由JS渲染上的,去分析其AJAX請求和構(gòu)造不是輕而易舉的事情忍坷,我決定引入JS渲染引擎粘舟,一勞永逸地解決。
大體有兩種方案佩研,在官方文檔的最下邊柑肴,Pre-rendering JavaScript
和Using a headless browser
,前者配合Splash
進(jìn)行JS渲染旬薯,后者使用了所謂的無頭(不顯示)瀏覽器晰骑,兩種方法我都做了嘗試。
Using a headless browser
首先評價(jià)一下這個(gè)方案绊序,考慮爬蟲效率的不推薦使用無頭瀏覽器硕舆,相比Splash它破壞了Scrapy的異步并行性秽荞,優(yōu)勢是它是個(gè)完全的瀏覽器,瀏覽器能做到的他都能做到抚官,模擬登陸較Splash簡單扬跋。更多對比:https://www.cnblogs.com/chaojiyingxiong/p/10246244.html
在百分之九十九的情況下,我都推薦使用Splash附魔JS渲染能力凌节,無論是從效率角度還是安裝環(huán)境的簡易程度上钦听。我之所以會(huì)嘗試了這個(gè)方案,是因?yàn)樵谖疑弦淮螌懪老x(2015年前)倍奢,還沒有Splash的方案朴上,我使用了selenium+phantomJS賦能Scrapy,這次也自然地想這么做娱挨。當(dāng)我完成之后余指,看到屏幕上Selenium報(bào)出一個(gè)warning捕犬,說不再支持phantomJS跷坝,請使用headless browser,我自然去搜索headless browser的方案碉碉,忽略了Splash柴钻。但作為一個(gè)可選方案,還是簡要記錄一下垢粮。
在我的mac上贴届,已經(jīng)安裝了chrome,還需要一個(gè)與之版本相應(yīng)的chromedriver蜡吧,其下載地址在https://sites.google.com/a/chromium.org/chromedriver/downloads 毫蚓,解壓到PATH下即可
在shell中輸入命令chromedriver 得到如下所示,即為安裝成功
Starting ChromeDriver 80.0.3987.106 (f68069574609230cf9b635cd784cfb1bf81bb53a-refs/branch-heads/3987@{#882}) on port 9515
另外需要安裝selenium 和 scrapy_selenium 昔善,前者用來控制webdriver元潘,后者是scrapy為了配合selenium實(shí)現(xiàn)的一系列組件。
pip install selenium scrapy_selenium
在settings.py總增加如下代碼君仆,其中chromedriver啟動(dòng)參數(shù)--no-sandbox在docker環(huán)境下是必要的翩概,如果沒有該選項(xiàng),由于在docker環(huán)境下是root用戶返咱,會(huì)提示不能再root用戶下啟動(dòng)钥庇。
from shutil import which
SELENIUM_DRIVER_NAME = 'chrome'
SELENIUM_DRIVER_EXECUTABLE_PATH = which('chromedriver')
SELENIUM_DRIVER_ARGUMENTS=['--headless','--no-sandbox','--disable-dev-shm-usage','blink-settings=imagesEnabled=false']
DOWNLOADER_MIDDLEWARES = {
'scrapy_selenium.SeleniumMiddleware': 800
}
在構(gòu)造請求時(shí)使用scrapy_selenium庫中的SeleniumRequest
from scrapy_selenium import SeleniumRequest
# yield scrapy.Request(url, callback=self.parse)
yield SeleniumRequest(url=url, callback=self.parse, script='window.scrollTo(0, document.body.scrollHeight);')
Pre-rendering JavaScript
需要先安裝Splash,并啟動(dòng)Splash服務(wù)咖摹,這個(gè)可以使用docker评姨,只要網(wǎng)速夠快,沒有任何坑萤晴。
docker run -p 8050:8050 scrapinghub/splash
看到Server listening on http://0.0.0.0:8050
即為啟動(dòng)成功
使用pip安裝scrapy_splash参咙,里面有和Scrapy配合的組件
pip install scrapy_splash
在settings.py中做如下配置龄广,其中SPLASH_URL指定了剛剛啟動(dòng)的Splash服務(wù)地址,DOWNLOADER_MIDDLEWARES和SPIDER_MIDDLEWARES替換一系列能與Splash配合的下載器蕴侧。
SPLASH_URL = 'http://localhost:8050'
DOWNLOADER_MIDDLEWARES = {
'scrapy_splash.SplashCookiesMiddleware': 723,
'scrapy_splash.SplashMiddleware': 725,
'scrapy.downloadermiddlewares.httpcompression.HttpCompressionMiddleware': 810,
}
SPIDER_MIDDLEWARES = {
'scrapy_splash.SplashDeduplicateArgsMiddleware': 100,
}
DUPEFILTER_CLASS = 'scrapy_splash.SplashAwareDupeFilter'
在構(gòu)造請求時(shí)择同,將scrapy.Request
替換為 SplashRequest
from scrapy_splash import SplashRequest
yield SplashRequest(url=url, callback=self.parse)
# yield scrapy.Request(url, callback=self.parse)
install in docker
Using a headless browser的方案相對復(fù)雜,在docker中安裝不易净宵,給出Dockerfile
FROM python:3.6-slim-stretch
# Install essential packages
RUN apt-get update -y \
&& apt-get -y install \
dumb-init gnupg wget ca-certificates apt-transport-https \
ttf-wqy-zenhei unzip \
&& rm -rf /var/lib/apt/lists/* /var/cache/apt/*
# Install Chrome Headless Browser
RUN wget -q -O - https://dl.google.com/linux/linux_signing_key.pub | apt-key add - \
&& echo "deb https://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google-chrome.list \
&& apt-get update -y \
&& apt-get -y install google-chrome-stable \
&& apt-get -y install build-essential libssl-dev libffi-dev libxml2-dev libxslt-dev \
&& rm /etc/apt/sources.list.d/google-chrome.list \
&& rm -rf /var/lib/apt/lists/* /var/cache/apt/*
# Install chromedriver
ARG CHROME_DRIVER_VERSION=2.35
RUN wget -O tmp/chromedriver_linux64.zip https://chromedriver.storage.googleapis.com/81.0.4044.69/chromedriver_linux64.zip \
&& unzip tmp/chromedriver_linux64.zip -d tmp \
&& rm tmp/chromedriver_linux64.zip \
&& chmod 755 tmp/chromedriver \
&& mv tmp/chromedriver /usr/bin/chromedriver
# Install scrapy & selenium & scrapy-selenium
RUN pip install scrapy selenium scrapy-selenium
CMD [ "/bin/bash" ]