重大跟新
:https://blog.csdn.net/pineapple_C/article/details/108181761post模擬登錄淘寶並爬取商品列表
像淘寶這類有着強大的反爬機制的網站來說,其網頁內容大多是用Ajax,JavaScript技術動態渲染出來的。如果用request庫,即便可以爬取到網頁的代碼,也不是網頁真正的代碼。
而使用selenium不僅可以爬取到網頁此時呈現的源代碼,還可以驅動瀏覽器進行指定的,模仿人類行為的操作,從而做到了可見即可爬。
一、准備工作
1、確保裝備有兩個第三方庫:selenium和pyquery
2、
(1)使用Firefox需要下載geckodriver
下載鏈接:https://github.com/mozilla/geckodriver
如果網速不好,可以選擇鏡像下載:https://npm.taobao.org/mirrors/geckodriver
如果瀏覽器是最新版本,選擇v0.26.0版本下載
(2)使用Chrome需要下載chromedriver
下載鏈接:https://chromedriver.storage.googleapis.com/index.html
如果瀏覽器是最新版本,選擇84.0.4147.30版本下載
(3)最好放在python/Script文件夾下,這樣就不需要配置環境變量。
二、初步分析
(1)了解爬取對象
查看淘寶網的robots協議:https://www.taobao.com/robots.txt
很好,他只針對百度爬蟲,我們是可以爬取的,當然這一步可以用程序判斷,后面再說。
(2)用戶登錄
如果單純的驅動瀏覽器訪問淘寶,是需要用戶登陸的。如果不登錄,就只能瀏覽主頁,不能購物
例如:
from selenium.webdriver.support.ui import WebDriverWait
from selenium import webdriver
from urllib.parse import quote
url='https://s.taobao.com/search?q='
# 初始化Firefox瀏覽器對象
browser=webdriver.Firefox()
# browser=webdriver.Chrome() # Chrome瀏覽器
browser.get(url+quote('python書')) # 在搜索欄輸入商品后的url
WebDriverWait(browser,10)
出現的頁面是這樣的
需要輸入用戶名和密碼,還需要滑塊驗證。除了這些,其它的驗證碼,文字識別,拼圖等高端操作都是可以用selenium庫搞定的。但是我還沒有研究到那里。
思考一下,有沒有其他的方法呢?單純的驅動瀏覽器是沒有任何用戶配置的,插件,瀏覽記錄,書簽都沒有,就是一個全新的瀏覽器。如果我之前就打開過淘寶並且登陸,淘寶網便會保存我的使用記錄,那何不直接驅動帶有用戶配置的瀏覽器呢?這樣就不需要再考慮偽裝頭部,cookies等問題。
例如:(Firefox瀏覽器)
from selenium.webdriver.support.ui import WebDriverWait
from selenium import webdriver
from urllib.parse import quote
# 配置文件地址
profile_directory = r'C:\Users\18368\AppData\Roaming\Mozilla\Firefox\Profiles\x9hlgi1i.dev-edition-default'
# 加載配置
profile = webdriver.FirefoxProfile(profile_directory)
# 啟動瀏覽器配置
browser = webdriver.Firefox(profile)
url='https://s.taobao.com/search?q='
browser.get(url+quote('python書'))
WebDriverWait(browser,10)
每個人的配置文件地址是不同的,在搜索框中輸入about:support即可看到自己的相關配置
運行后出現的頁面是這樣的:
可以發現書簽,用戶信息等都在
如果使用Chrome瀏覽器的話,有兩種方式
方式1):控制已經打開的Chrome
cmd輸入以下命令:
C:\Program Files (x86)\Google\Chrome\Application>chrome.exe --remote-debugging-port=9222 --user-data-dir="C:\selenum\AutomationProfile"
打開一個遠程調試端口(端口自己定),在本地創建一個Chrome配置目錄,這樣就不會影響源目錄。
在上述命令打開的瀏覽器里訪問淘寶,登陸並搜索python書
然后運行如下代碼
from selenium.webdriver.chrome.options import Options
from selenium import webdriver
# 初始化瀏覽器選項對象
chrome_options = Options()
# 添加拓展選項,指定調試端口
chrome_options.add_experimental_option("debuggerAddress", "127.0.0.1:9222")
# driver = webdriver.Chrome(chrome_options=chrome_options)
# selenium3.141.0 python3.8.1出現棄用警告
# DeprecationWarning: use options instead of chrome_options
# 將chrome_options換成options
driver = webdriver.Chrome(options=chrome_options)
print(driver.find_element_by_id('J_Itemlist_TLink_564162841422').text)
輸出結果:下單優惠正版 python基礎教程 零基礎學Python編程從入門到實踐計算機程序設計pathon3核心技術網絡爬蟲書籍數據分析實戰教程教材(第一本書的書名)
驗證通過,可以使用這種方法。
方式2):加載所有Chrome瀏覽器配置
from selenium.webdriver.support.ui import WebDriverWait
from selenium import webdriver
option = webdriver.ChromeOptions()
p = r'C:\Users\18368\AppData\Local\Google\Chrome\User Data'
option.add_argument('--user-data-dir=' + p) # 設置成用戶自己的數據目錄
driver = webdriver.Chrome(options=option) # chrome_options已棄用
driver.get('https://www.taobao.com')
WebDriverWait(driver,10)
運行前如果Chrome瀏覽器已開啟,會出現invalid argument: user data directory is already in use, please specify a unique value for --user-data-dir argument, or don't use --user-data-dir
的錯誤,原因是配置目錄已經在使用,所以運行前要關閉Chrome。
運行結果:下單優惠正版 python基礎教程 零基礎學Python編程從入門到實踐計算機程序設計pathon3核心技術網絡爬蟲書籍數據分析實戰教程教材(第一本書的書名)
Firefox和Chrome一共三種方法。本文選擇用Firefox。(使用Chrome瀏覽器的話,可以使用上文的兩種方法)
三、編寫程序
1、相關依賴
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait
from selenium.common.exceptions import TimeoutException
from selenium.webdriver.common.by import By
from pyquery.pyquery import PyQuery as pq
from urllib.parse import quote
from selenium import webdriver
import json
inport | 作用 |
---|---|
EC | 等待條件 |
WebDriverWait | 設置最長等待時間 |
TimeoutException | 超時異常 |
By | 查找結點的條件 |
pq | 適合使用CSS選擇器的一個網頁解析庫 |
quote | 將參數轉化為URL編碼格式,解決中文參數亂碼問題 |
webdriver | 初始化瀏覽器對象 |
json | 以json的形式保存文件 |
2、全局變量
# 配置文件地址
profile_directory = r'C:\Users\18368\AppData\Roaming\Mozilla\Firefox\Profiles\x9hlgi1i.dev-edition-default'
# 加載配置
profile = webdriver.FirefoxProfile(profile_directory)
# 啟動瀏覽器配置
browser = webdriver.Firefox(profile)
# 設置等待時間,10s
wait = WebDriverWait(browser, 10)
KEYWORD = 'Python書' # 爬取對象
RESULT = [] # 爬取結果
MAX_PAGE = 10 # 爬取頁數
3、翻頁函數
def index_page(page):
"""
執行翻頁的操作,如果出錯,則重新執行
:param page: 頁碼
:return: 無
"""
print('正在爬取第{}頁'.format(page))
try:
url = 'https://s.taobao.com/search?q=' + quote(KEYWORD)
browser.get(url)
# 保存爬取到的網頁源代碼,以對比驗證是否正確
# html=browser.page_source
# with open('taobao.html','w',encoding='utf-8')as file:
# file.write(html)
if page > 1:
# 等待class屬性值為 'J_Input'的結點加載出來
input = wait.until(
EC.presence_of_element_located((By.CLASS_NAME, 'J_Input'))
)
# 等待class屬性值為'J_Submit'的結點可以被點擊
submit = wait.until(
EC.element_to_be_clickable((By.CLASS_NAME, 'J_Submit'))
)
# 通過JavaScript修改結點的屬性值
browser.execute_script("arguments[0].setAttribute('value','{}');".format(str(page)), input)
submit.click()
# 等待網頁全部加載完成,使用CSS選擇器查找
wait.until(EC.text_to_be_present_in_element((
By.CSS_SELECTOR, '#mainsrp-pager li.item.active > span'), str(page)
))
wait.until(EC.presence_of_element_located((
By.CSS_SELECTOR, '.m-itemlist .items'
)))
get_products()
except TimeoutException:
index_page(page)
首次運行可以去掉注釋部分的代碼,設置頁碼page為1,用以檢驗爬取結果是否正確。
if條件語句用來判斷當頁碼大於1時,進行翻頁操作。
首先設定最大等待時間,input:找到 “跳轉” 結點;submit:等待“確定”結點可以被點擊。
我們可以在爬取結果中搜索 “跳轉” 結點的class屬性 'J_Input',順便還可以檢驗爬取結果的正確性
發現只有 “跳轉” 結點有這個屬性值,所以我們可以根據class屬性來查找,同理 “確定” 結點也是如此。
接着找到跳轉的輸入框,只發現了到第和頁,就是沒有中間的頁碼
多翻幾頁可以找到規律,頁碼是夾在結點的value屬性里的,是通過JavaScript修改的。需要利用execute_script()方法模擬執行JavaScript操作。
這里沒有采用點擊下一頁的操作,這樣翻頁雖然簡單,但是如果出現異常,上一頁和下一頁的按鈕都沒有加載出來,就無法重新開始這一頁和繼續下一頁,程序也會因此退出。而且在翻頁的同時還要記錄頁數,這就有點不方便了。所以采用跳轉頁面的方式,這樣即使出現異常也可以快速的重新開始。
最后根據當前頁碼變色和商品信息加載完畢來判斷網頁加載完成。因為結點的屬性並不唯一且位置結構比較復雜,所以用CSS選擇器來查找結點。確定無誤后,進入下一個函數get_products()進行信息篩選。
4、篩選信息
def get_products():
"""
使用puquery篩選商品的圖片、價格、成交人數等信息
:return: 無
"""
html = browser.page_source
doc = pq(html)
# 找到本頁商品列表的結點
items = doc('#mainsrp-itemlist .item').items()
for item in items:
product = {
'image': 'https:' + item.find('.pic .img').attr('data-src'),
'price': item.find('.price').text(),
'deal': item.find('.deal-cnt').text()[:-3],
'title': item.find('.title').text(),
'shop': item.find('.shop').text(),
'location': item.find('.location').text()
}
RESULT.append(product)
5、信息保存
def save_to_json():
"""
將爬取下來的信息以json的形式保存
:return: 無
"""
print('\n爬取工作完成,一共{}個商品'.format(len(RESULT)))
print('開始寫入文件taobao_{}.json......'.format(KEYWORD))
with open('taobao_{}.json'.format(KEYWORD), 'w', encoding='utf-8')as file:
file.write(json.dumps(RESULT, ensure_ascii=False))
6、遍歷每一頁
def main():
"""
遍歷每一頁
:return: 無
"""
for i in range(1, MAX_PAGE + 1):
index_page(i)
save_to_json()
四、完整代碼
基於PyCarm、python3.8.1、selenium3.141.0、pyquery1.4.1
# -*- coding: utf-8 -*-
"""
@author:Pineapple
@contact:cppjavapython@foxmail.com
@time:2020/7/19 17:05
@file:taobao.py
@desc:Use python web crawler to get product information on Taobao,
learn from https://github.com/Python3WebSpider/TaobaoProduct.
"""
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait
from selenium.common.exceptions import TimeoutException
from selenium.webdriver.common.by import By
from pyquery.pyquery import PyQuery as pq
from urllib.parse import quote
from selenium import webdriver
import json
# 配置文件地址
profile_directory = r'C:\Users\18368\AppData\Roaming\Mozilla\Firefox\Profiles\x9hlgi1i.dev-edition-default'
# 加載配置
profile = webdriver.FirefoxProfile(profile_directory)
# 啟動瀏覽器配置
browser = webdriver.Firefox(profile)
# 設置等待時間,10s
wait = WebDriverWait(browser, 10)
KEYWORD = 'Python書' # 爬取對象
RESULT = [] # 爬取結果
MAX_PAGE = 10 # 爬取頁數
def index_page(page):
"""
執行翻頁的操作,如果出錯,則重新執行
:param page: 頁碼
:return: 無
"""
print('正在爬取第{}頁'.format(page))
try:
url = 'https://s.taobao.com/search?q=' + quote(KEYWORD)
browser.get(url)
# 保存爬取到的網頁源代碼,以對比驗證是否正確
# html=browser.page_source
# with open('taobao.html','w',encoding='utf-8')as file:
# file.write(html)
if page > 1:
# 等待class屬性為'J_Input'的結點加載出來
input = wait.until(
EC.presence_of_element_located((By.CLASS_NAME, 'J_Input'))
)
# 等待class屬性值為'J_Submit'的結點可以被點擊
submit = wait.until(
EC.element_to_be_clickable((By.CLASS_NAME, 'J_Submit'))
)
# 通過JavaScript修改結點的屬性值
browser.execute_script("arguments[0].setAttribute('value','{}');".format(str(page)), input)
submit.click()
# 等待網頁全部加載完成,使用CSS選擇器查找
wait.until(EC.text_to_be_present_in_element((
By.CSS_SELECTOR, '#mainsrp-pager li.item.active > span'), str(page)
))
wait.until(EC.presence_of_element_located((
By.CSS_SELECTOR, '.m-itemlist .items'
)))
get_products()
except TimeoutException:
index_page(page)
def get_products():
"""
篩選商品的圖片、價格、成交人數等信息
:return: 無
"""
html = browser.page_source
doc = pq(html)
# 找到本頁商品列表的結點
items = doc('#mainsrp-itemlist .item').items()
for item in items:
product = {
'image': 'https:' + item.find('.pic .img').attr('data-src'),
'price': item.find('.price').text(),
'deal': item.find('.deal-cnt').text()[:-3],
'title': item.find('.title').text(),
'shop': item.find('.shop').text(),
'location': item.find('.location').text()
}
RESULT.append(product)
def save_to_json():
"""
將爬取下來的信息以json的形式保存
:return: 無
"""
print('\n爬取工作完成,一共{}個商品'.format(len(RESULT)))
print('開始寫入文件taobao_{}.json......'.format(KEYWORD))
with open('taobao_{}.json'.format(KEYWORD), 'w', encoding='utf-8')as file:
file.write(json.dumps(RESULT, ensure_ascii=False))
def main():
"""
遍歷每一頁
:return: 無
"""
for i in range(1, MAX_PAGE + 1):
index_page(i)
save_to_json()
if __name__ == '__main__':
main()
結果展示(以python書為例)
我們可以計算一下數目來驗證是不是爬取了10頁,除了第一頁是12行+4列外,其他都是11行加4列,11 * 4 * 9 + 12 * 4 = 444。正好!
剛打開文件時會有一點亂,按下CTRL+ALT+L代碼對齊即可。
五、結語
這幾天看崔慶才老師的書《Python3網絡爬蟲開發實戰》,受益頗深。
這本書已經出版三年了,當時的網頁源碼與現在有些不同,淘寶的反爬機制也是越來越強大。於是為了還是能爬取到相關信息,我在結合了已學知識的同時,又拓展一些selenium庫的其他操作,對源代碼進行了修改。
當然,也有一些大神對源碼做了修改,並且放到了issue里。甚至還有用微博登陸的奇葩思路。
github鏈接:https://github.com/Python3WebSpider/TaobaoProduct
分享一下崔老師的博客:https://cuiqingcai.com/