Python網絡爬蟲-使用Selenium爬取京東商品

Python網絡爬蟲-模擬Ajax請求抓取微博中我們瞭解了Ajax的分析和抓取的方式,但是有很多的網站即使是Ajax來獲取的數據,但是其Ajax接口含有很多加密參數,我們很難找出其中的規律,也就很難直接使用Ajax來抓取。

爲了解決這些問題,我們可以直接使用模擬瀏覽器運行的方式來實現,這樣就可以做到在瀏覽器中看到是什麼樣,抓取的源碼就是什麼樣,也就是可見即可爬。這樣我們就不用再去管網頁內部的JavaScript用了什麼算法渲染頁面,不用管網頁後臺的Ajax接口到底有哪些參數。

Python提供了許多模擬瀏覽器運行的庫,如Selenium、Splash等。

Selenium是一個自動化測試工具,利用它可以驅動瀏覽器執行特定的動作,如點擊、下拉等操作,同時還可以獲取瀏覽器當前呈現的頁面的源代碼,做到可見即可爬。對於一些JavaSript動態渲染的頁面來說,此種抓取方式非常有效。

下面使用Selenium來模擬瀏覽器操作爬取京東商城的商品信息。

1.目標

使用Selenium爬取京東商城商品信息並使用xpath解析得到商品的名稱、價格、評論和店鋪名稱並將其寫入文件保存。

2.準備工作

  • Chrome瀏覽器
  • 安裝Python的Selenium庫
  • 在Chrome裏面配置ChromeDriver

3.網頁分析

人工輸入京東的網址https://www.jd.com/,然後在搜索框中輸入ipad點擊確認,往下拉到網頁底部,頁面上共加載出60個有關ipad關鍵字的商品信息,並且在頁面底部有一個分頁的導航,其中既包括了前7頁的鏈接,頁包括了下一頁的鏈接。

4.爬取流程

  • 0基礎url:https://www.jd.com/
  • 1使用selenium模擬瀏覽器,定位到搜索框,輸入ipad關鍵字,然後點擊確定
  • 2等待網頁加載,獲取網頁代碼
  • 3解析網頁源代碼,獲取商品信息寫入文件
  • 4以上流程結束,使用selenium定位到下一頁的按鈕,模擬點擊
  • 5等待網頁加載,重複以上234,直到獲取到所有的頁面的商品信息

5.使用selenium模擬爬取流程中的12流程

from selenium import webdriver
from selenium.common.exceptions import TimeoutException
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.wait import WebDriverWait
from lxml import etree

browser = webdriver.Chrome()
wait = WebDriverWait(browser, 10)
KEY_WORD = 'ipad'


# 流程1開始準備工作,獲取到基礎url,模擬瀏覽器輸入ipad,點確定
def start_crawl():
    try:
        url = 'https://www.jd.com/'
        browser.get(url)
        # 根據CSS定位到輸入搜索框
        input = wait.until(EC.presence_of_all_elements_located((By.CSS_SELECTOR, '#key')))
        # 根據CSS定位到確定按鈕
        submit = wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, '#search > div > div.form > button')))
        type(input)
        input[0].send_keys(KEY_WORD)
        submit.click()
    except TimeoutException:
        print('start_crawl exception')


def next_page(page):
    print("爬取第", page, "頁")
    try:
        # 滑動到底部,不然頁面只有30個商品信息
        browser.execute_script("window.scrollTo(0, document.body.scrollHeight);")
        time.sleep(5)
        # 加載完成,獲取當前頁的網頁資源
        html = browser.page_source
        # todo 解析當前頁
        # todo 保存到文件
        # 開始翻頁 獲取下一頁的按鈕
        button = wait.until(
            EC.element_to_be_clickable((By.CSS_SELECTOR, '#J_bottomPage > span.p-num > a.pn-next > em')))
        button.click()
        # 需要增加翻頁成功的標示?
    except TimeoutException:
        print('next_page exception', page)

6.解析商品信息

根據源碼中商品信息的佈局,解析商品的信息代碼如下:

from lxml import etree

def parse_html(html):
    html = etree.HTML(html)
    # 獲取所有樣式是li class名是gl-item的html文件
    items = html.xpath('//li[@class="gl-item"]')
    for i in range(len(items)):
        product = {}
        # 標題是:div子孫節點 class名是p-name p-name-type-2 子孫節點em下的所有文本
        product['title'] = html.xpath('//div[@class="p-name p-name-type-2"]//em')[i].xpath('string(.)')
        # 價格是: div子孫節點 class名是p-price 子孫節點i下的文本
        product['price'] = html.xpath('//div[@class="p-price"]//i')[i].text
        # 評論是 div子孫節點 class是p-commit 子節點strong 子節點a下的文本 
        product['comment'] = html.xpath('//div[@class="p-commit"]/strong/a')[i].text
        product['shop'] = html.xpath('//div[@class="p-shop"]//a')[i].text
        yield product

7.整體代碼

# -*- coding: utf-8 -*-
# @Time    : 2019-08-02 22:07
# @Author  : xudong
# @email   : [email protected]
# @Site    : 
# @File    : jdSpider.py
# @Software: PyCharm

'''
jd的搜索頁面,爬取關鍵字是ipad的商品信息
'''

from selenium import webdriver
from selenium.common.exceptions import TimeoutException
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.wait import WebDriverWait
from lxml import etree
import json
import time

browser = webdriver.Chrome()
wait = WebDriverWait(browser, 10)
KEY_WORD = 'ipad'


# 流程1開始準備工作,獲取到基礎url,模擬瀏覽器輸入ipad,點確定
def start_crawl():
    try:
        url = 'https://www.jd.com/'
        browser.get(url)
        # 根據CSS定位到輸入搜索框
        input = wait.until(EC.presence_of_all_elements_located((By.CSS_SELECTOR, '#key')))
        # 根據CSS定位到確定按鈕
        submit = wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, '#search > div > div.form > button')))
        type(input)
        input[0].send_keys(KEY_WORD)
        submit.click()
    except TimeoutException:
        print('start_crawl exception')


def get_total_page():
    # 獲取所有的頁碼數
    total = wait.until(
        EC.presence_of_all_elements_located(
            (By.CSS_SELECTOR, '#J_bottomPage > span.p-skip > em:nth-child(1) > b')
        )
    )
    return total[0].text


# 加載當前頁解析寫入文件並獲取下一頁
def next_page(page):
    print("爬取第", page, "頁")
    try:
        # 滑動到底部,不然頁面只有30個商品信息
        browser.execute_script("window.scrollTo(0, document.body.scrollHeight);")
        # 一定要加time sleep不然網頁加載不出來獲取不到數據
        time.sleep(5)
        # 加載完成,獲取當前頁的網頁資源
        html = browser.page_source
        # 解析當前頁
        results = parse_html(html)
        # 保存到文件
        write_file(results)
        # 開始翻頁 獲取到下一頁的按鈕
        button = wait.until(
            EC.element_to_be_clickable((By.CSS_SELECTOR, '#J_bottomPage > span.p-num > a.pn-next > em')))
        button.click()
        # 需要增加翻頁成功的標示?
    except TimeoutException:
        print('next_page exception', page)


# 解析網頁
def parse_html(html):
    html = etree.HTML(html)
    # 獲取所有樣式是li class名是gl-item的html文件
    items = html.xpath('//li[@class="gl-item"]')
    for i in range(len(items)):
        product = {}
        # 標題是:div子孫節點 class名是p-name p-name-type-2 子孫節點em下的所有文本
        product['title'] = html.xpath('//div[@class="p-name p-name-type-2"]//em')[i].xpath('string(.)')
        # 價格是: div子孫節點 class名是p-price 子孫節點i下的文本
        product['price'] = html.xpath('//div[@class="p-price"]//i')[i].text
        # 評論是 div子孫節點 class是p-commit 子節點strong 子節點a下的文本 
        product['comment'] = html.xpath('//div[@class="p-commit"]/strong/a')[i].text
        product['shop'] = html.xpath('//div[@class="p-shop"]//a')[i].text
        yield product

# 寫入文件
def write_file(results):
    for result in results:
        print(result)
        json1 = json.dumps(result, ensure_ascii=False) + '\n'
        with open('jd.txt', 'a', encoding='utf-8') as file:
            file.write(json1)


if __name__ == '__main__':
    start_crawl()
    # 爬10頁,可以利用get_total_page方法爬取所有頁
    for i in range(10):
        next_page(i)


8.結果

如果控制檯能夠打印數據,並且結束後,對應文件中有jd.txt文件,文件中有如下的數據,即表示目標達成。

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章