1. 程式人生 > >小白學 Python 爬蟲(40):爬蟲框架 Scrapy 入門基礎(七)對接 Selenium 實戰

小白學 Python 爬蟲(40):爬蟲框架 Scrapy 入門基礎(七)對接 Selenium 實戰

人生苦短,我用 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

示例程式碼-Gi