
人生苦短,我用 Python
前文傳送門:
小白學 Python 爬蟲(1):開篇
小白學 Python 爬蟲(2):前置準備(一)基本類庫的安裝
小白學 Python 爬蟲(3):前置準備(二)Linux基礎入門
小白學 Python 爬蟲(4):前置準備(三)Docker基礎入門
小白學 Python 爬蟲(5):前置準備(四)資料庫基礎
小白學 Python 爬蟲(6):前置準備(五)爬蟲架構的安裝
小白學 Python 爬蟲(7):HTTP 基礎
小白學 Python 爬蟲(8):網頁基礎
小白學 Python 爬蟲(9):爬蟲基礎
小白學 Python 爬蟲(10):Session 和 Cookies
小白學 Python 爬蟲(11):urllib 基礎使用(一)
小白學 Python 爬蟲(12):urllib 基礎使用(二)
小白學 Python 爬蟲(13):urllib 基礎使用(三)
小白學 Python 爬蟲(14):urllib 基礎使用(四)
小白學 Python 爬蟲(15):urllib 基礎使用(五)
小白學 Python 爬蟲(16):urllib 實戰之爬取妹子圖
小白學 Python 爬蟲(17):Requests 基礎使用
小白學 Python 爬蟲(18):Requests 進階操作
小白學 Python 爬蟲(19):Xpath 基操
小白學 Python 爬蟲(20):Xpath 進階
小白學 Python 爬蟲(21):解析庫 Beautiful Soup(上)
小白學 Python 爬蟲(22):解析庫 Beautiful Soup(下)
小白學 Python 爬蟲(23):解析庫 pyquery 入門
小白學 Python 爬蟲(24):2019 豆瓣電影排行
小白學 Python 爬蟲(25):爬取股票資訊
小白學 Python 爬蟲(26):為啥買不起上海二手房你都買不起
小白學 Python 爬蟲(27):自動化測試架構 Selenium 從入門到放棄(上)
小白學 Python 爬蟲(28):自動化測試架構 Selenium 從入門到放棄(下)
小白學 Python 爬蟲(29):Selenium 擷取某大型電商網站商品資訊
小白學 Python 爬蟲(30):代理基礎
小白學 Python 爬蟲(31):自己建構一個簡單的代理池
小白學 Python 爬蟲(32):異步請求庫 AIOHTTP 基礎入門
小白學 Python 爬蟲(33):爬蟲架構 Scrapy 入門基礎(一)
小白學 Python 爬蟲(34):爬蟲架構 Scrapy 入門基礎(二)
小白學 Python 爬蟲(35):爬蟲架構 Scrapy 入門基礎(三) Selector 選擇器
小白學 Python 爬蟲(36):爬蟲架構 Scrapy 入門基礎(四) Downloader Middleware
小白學 Python 爬蟲(39): JavaScript 渲染服務 Scrapy-Splash 入門
引言
Scrapy 抓取頁面的方式和 Requests 類庫是一樣的,都是直接模拟 HTTP 請求,對于由 JavaScript 動态渲染的頁面就有些顯得無能為力了。
我們前面抓取由 JavaScript 動态渲染的頁面是使用 Selenium 對接浏覽器進行頁面抓取,當然,在 Scrapy 中同樣也可以對接 Selenium 。
通過這種方案,我們無需關心一個頁面加載是發送的請求,也無需關注頁面的渲染過程,直接抓取最終結果就行,真正做到了可見即可抓。
示例
小目标
首先定一個小目标,前面的文章我們通過 Selenium 抓取了某東的商品資訊,本篇我們依然使用這個站點,感謝某東為我們提供的素材。
準備
請各位同學确認自己本地已經正确安裝 Scrapy 、 Selenium 以及 Selenium 所需要使用的一些驅動庫,如果尚未安裝的同學可以翻翻前面的文章。
建立項目
本篇内容還是建立一個新的 Scrapy 項目,并且命名為 scrapy_selenium_demo ,指令如下:
scrapy startproject scrapy_selenium_demo
記得找一個自己喜歡的目錄,最好是純英文目錄。
然後建立一個 Spider ,指令如下:
scrapy genspider jd www.jd.com
記得順手修改下 settings.py 中的配置,将 robots.txt 設定為 False ,否則我們無法抓取到相關的商品資料,因為在機器人協定中某東并不允許抓取商品資料,修改如下:
ROBOTSTXT_OBEY = False
定義資料結構
第一步還是我們将要抓取的資料結構定義到 Item ,代碼如下:
import scrapy
class ProductItem(scrapy.Item):
collection = 'products'
image = scrapy.Field()
price = scrapy.Field()
name = scrapy.Field()
commit = scrapy.Field()
shop = scrapy.Field()
icons = scrapy.Field()
這裡我們定義了 6 個字段,和之前的示例完全相同,然後定一個了 collection ,這個是用于儲存進資料的表的名稱。
Spider
接下來,是我們的 Spider 的定義,先初步的定義一個 start_requests() 方法,後續還會有修改,示例如下:
# -*- coding: utf-8 -*-
from scrapy import Request, Spider
class JdSpider(Spider):
name = 'jd'
allowed_domains = ['www.jd.com']
start_urls = ['http://www.jd.com/']
def start_requests(self):
base_url = 'https://search.jd.com/Search?keyword=iPhone&ev=exbrand_Apple'
headers = {
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.117 Safari/537.36',
'referer': 'https://www.jd.com/'
}
for page in range(1, self.settings.get('MAX_PAGE') + 1, 2):
url = base_url + '&page=' + str(page)
yield Request(url=url, callback=self.parse, headers = headers)
最大的頁碼使用 MAX_PAGE 來表示,同樣的這個配置需要添加至 settings.py 配置檔案,如下:
MAX_PAGE = 3
在 start_requests() 中,我們通過 url 位址拼接的方式,周遊出來了所有我們需要通路的頁面,因為某東的商品頁面的翻頁規則,這裡我們使用的步長為 2 。
對接 Selenium
接下來我們需要對這些請求進行資料抓取,這裡我們通過對接 Selenium 來完成。
具體的實作方案是使用 Download Middleware 來完成對接。示例代碼如下:
# -*- coding: utf-8 -*-
# Define here the models for your spider middleware
#
# See documentation in:
# https://docs.scrapy.org/en/latest/topics/spider-middleware.html
from selenium import webdriver
from selenium.common.exceptions import TimeoutException
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from scrapy.http import HtmlResponse
from logging import getLogger
class SeleniumMiddleware(object):
def __init__(self, timeout=None, service_args=[]):
self.logger = getLogger(__name__)
self.timeout = timeout
# Chrome 開啟無視窗模式
chrome_options = webdriver.ChromeOptions()
chrome_options.add_argument('--headless')
self.driver = webdriver.Chrome(service_args=service_args, chrome_options=chrome_options)
self.driver.set_window_size(1400, 700)
self.driver.implicitly_wait(self.timeout)
self.driver.set_page_load_timeout(self.timeout)
self.wait = WebDriverWait(self.driver, self.timeout)
def __del__(self):
self.driver.close()
def process_request(self, request, spider):
self.logger.debug('Chrome is Starting')
try:
page = request.meta.get('page', 1)
self.driver.get(request.url)
if page > 1:
input = self.wait.until(
EC.presence_of_element_located((By.XPATH, '//*[@id="J_bottomPage"]/span[2]/input')))
button = self.wait.until(
EC.element_to_be_clickable((By.XPATH, '//*[@id="J_bottomPage"]/span[2]/a')))
input.clear()
input.send_keys(page)
button.click()
return HtmlResponse(url=request.url, body=self.driver.page_source, request=request, encoding='utf-8',
status=200)
except TimeoutException:
return HtmlResponse(url=request.url, status=500, request=request)
@classmethod
def from_crawler(cls, crawler):
return cls(timeout=crawler.settings.get('SELENIUM_TIMEOUT'),
service_args=crawler.settings.get('CHROME_SERVICE_ARGS'))
寫完 Download Middleware 需在 settings.py 中增加 Download Middleware 的相關配置,如下:
DOWNLOADER_MIDDLEWARES = {
'scrapy_selenium_demo.middlewares.SeleniumMiddleware': 543,
}
解析頁面
我們在 Download Middleware 中獲得了 HtmlResponse ,這時需要在 Spider 中進行解析,如下:
def parse(self, response):
products = response.css('#J_goodsList .gl-item .gl-i-wrap')
for product in products:
item = ProductItem()
item['image'] = product.css('.p-img a img::attr("src")').extract_first()
item['price'] = product.css('.p-price i::text').extract_first()
item['name'] = product.css('.p-name em::text').extract_first()
item['commit'] = product.css('.p-commit a::text').extract_first()
item['shop'] = product.css('.p-shop a::text').extract_first()
item['icons'] = product.css('.p-icons .goods-icons::text').extract_first()
yield item
儲存 MongoDB
我們增加一個 ITEM_PIPELINES MongoPipeline 将資料儲存至 MongoDB ,如下:
import pymongo
class MongoPipeline(object):
def __init__(self, mongo_uri, mongo_db):
self.mongo_uri = mongo_uri
self.mongo_db = mongo_db
@classmethod
def from_crawler(cls, crawler):
return cls(mongo_uri=crawler.settings.get('MONGO_URI'),
mongo_db=crawler.settings.get('MONGO_DB')
)
def open_spider(self, spider):
self.client = pymongo.MongoClient(self.mongo_uri)
self.db = self.client[self.mongo_db]
def process_item(self, item, spider):
name = item.__class__.__name__
self.db[name].insert(dict(item))
return item
def close_spider(self, spider):
self.client.close()
在 settings 中新增相關配置:
ITEM_PIPELINES = {
'scrapy_selenium_demo.pipelines.MongoPipeline': 300,
}
至此,我們就完成主體程式,可以使用以下指令運作這隻爬蟲:
scrapy crawl jd
結果小編就不貼了,代碼已上傳代碼倉庫,有興趣的同學可以通路代碼倉庫擷取。
示例代碼
本系列的所有代碼小編都會放在代碼管理倉庫 Github 和 Gitee 上,友善大家取用。
示例代碼-Github
示例代碼-Gitee
掃描二維碼關注「極客挖掘機」公衆号!作者:極客挖掘機
定期發表作者的思考:技術、産品、營運、自我提升等。
本文版權歸作者極客挖掘機和部落格園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接配接,否則保留追究法律責任的權利。
如果您覺得作者的文章對您有幫助,就來作者個人小站逛逛吧:
極客挖掘機