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文件,文件中有如下的數據,即表示目標達成。