selenium+chrome抓取淘寶寶貝-崔慶才思路

站點分析

  • 看了交互,好複雜
  • 看了下Ajax,好複雜
  • 看了下其餘內容,看不懂...

因此,沒啥好分析的,直接上selenium吧html

源碼及遇到的問題

在搜索時,會跳轉到登陸界面

這個沒有辦法,是淘寶的反爬蟲機制. 由於經過selenium webdriver調用的瀏覽器會有不少異於正常瀏覽器的參數,具體生成了啥參數,咱也沒看懂.
具體的能夠參考下面這個大姐的文章python

分析淘寶登錄對selenium爬蟲的封殺方案,反爬蟲機制的升級git

並且阿里不愧是阿里,哪怕webdriver調用的chrome中輸入用戶名和密碼依舊不能夠.github

網上查了一下,基本是selenium是被封的死死的,基本上比較靠譜的方法就是使用pyppeteer庫.
那麼問題來了...web

  1. 我此次就是玩selenium的,臨陣換庫,很差.

好了,總結了這麼多,最終,發現了淘寶的一個bug. 雖然用戶名密碼登陸的方式會因爲ua值校驗等問題被拒絕. 可是掃碼登陸不會...chrome

因此個人解決思路很土,先掃碼登陸,拿到cookie,而後調用chrome以前,先把cookie寫進去. (注意!這裏有個坑,很大的坑) 若是不出意外的話,應該是能夠的.數據庫

step1:幹起來! 先取cookie

  1. def get_taobao_cookies(): 
  2. url = 'https://www.taobao.com/' 
  3. browser.get('https://login.taobao.com/') 
  4. while True: 
  5. print("please login to Taobao!") 
  6. # 這裏等一下下 
  7. time.sleep(4) 
  8. # 等到界面跳轉到首頁以後,下手 
  9. while browser.current_url == url: 
  10. tbCookies = browser.get_cookies() 
  11. browser.quit() 
  12. output_path = open('taobaoCookies.pickle', 'wb') 
  13. pickle.dump(tbCookies, output_path) 
  14. output_path.close() 
  15. return tbCookies 
  16.  

知識補充:pickle模塊數組


python的pickle模塊實現了基本的數據序列和反序列化。
經過pickle模塊的序列化操做咱們可以將程序中運行的對象信息保存到文件中去,永久存儲。
經過pickle模塊的反序列化操做,咱們可以從文件中建立上一次程序保存的對象。
基本接口:
pickle.dump(obj, file, [,protocol])
有了 pickle 這個對象, 就能對 file 以讀取的形式打開:
x = pickle.load(file)瀏覽器

取cookie卻是沒什麼問題. 問題是,這是我第一次見到原始的cookie,有點懵. 仔細看了以後才搞懂:微信

  • 取出的cookie是一個數組
  • 數組的每一個元素是一個cookie
  • 每一個cookie又是一個字典,其中記錄這這個cookie的 domian,key,value,path等等屬性.

這裏我用pickle.dump()方法把cookie存儲下來了. 下次使用的時候,直接load一下就行了.

step2:載入cookie

載入的話分爲兩部分:

第一部分:從文件中讀取cookie
這個很簡單,不作過多描述

  1. def read_taobao_cookies(): 
  2. if os.path.exists('taobaoCookies.pickle'): 
  3. read_path = open('taobaoCookies.pickle', 'rb') 
  4. tbCookies = pickle.load(read_path) 
  5. else: 
  6. tbCookies = get_taobao_cookies() 
  7. return tbCookies 
  8.  

第二部分:講cookie載入chrome

這個可把我坑慘了.

先看代碼,在search()方法中定義瞭如何載入cookie

  1. cookies = read_taobao_cookies() 
  2. # add_cookie以前要先打開一下網頁,否則他媽的會報invalid domain錯誤. 日了狗了 
  3. browser.get('https://www.taobao.com') 
  4. for cookie in cookies: 
  5. # stackoverflow查到的,不知道爲啥,要把expiry這個鍵值對刪掉,否則的話,會報invalid argument,MD! 
  6. if 'expiry' in cookie: 
  7. del cookie['expiry'] 
  8. browser.add_cookie(cookie) 
  9.  

這裏須要注意的有兩點:

  1. 在調用add_cookie()方法以前,必須先打開一個網頁.否則的話就會報InvalidCookieDomainException 的錯誤.
  2. cookie中的'expiry'屬性要刪除,否則會報invalid argument: invalid 'expiry'

可是看了下API,add_cookie()是支持這個expiry這個參數的
add_cookie()方法

後來查了一下,當前chromedriver對於expiry只支持int64,不支持double. 聽說是chromedriver的一個bug,在後續版本中會修復.

詳細回答參見這個問題下的高票答案

關於add_cookie時,expiry參數報錯的問題

step3:放飛自我

這兩個問題解決了以後,基本上剩下的都不是什麼大問題了. 這裏說一個以前不知道的小技巧,chrome瀏覽器在源碼審查的時候,能夠選中頁面元素,直接右鍵複製CSS選擇器
這個功能還挺好使的. 表示以前並不知道...

Chrome的CSS選擇器
Chrome的CSS選擇器

關於phantomJS瀏覽器的問題

在使用selenium的時候,若是不想看到瀏覽器界面,但是使用 phantomJS這個無界面的瀏覽器來代替. 可是看到pycharm報了個warning. 說是phantomJS已經被depressed. 建議使用headless chrome替代.

因而看了一眼headless chrome怎麼用. 很簡單,在調用chrome的時候傳入一個參數便可.

  1. chrome_options = Options() 
  2. chrome_options.add_argument('--headless') 
  3. browser = webdriver.Chrome(options=chrome_options) 
  4.  

源碼

源碼已上傳github,有須要的請直接下載.

  1. import os 
  2. import pickle 
  3. import re 
  4. import time 
  5.  
  6. from pyquery import PyQuery as pq 
  7. from selenium import webdriver 
  8. from selenium.common.exceptions import TimeoutException 
  9. from selenium.webdriver.common.by import By 
  10. from selenium.webdriver.support import expected_conditions as EC 
  11. from selenium.webdriver.support.ui import WebDriverWait 
  12. import pymongo 
  13. from config import * 
  14.  
  15. #鏈接數據庫 
  16. client = pymongo.MongoClient(MONGO_URL) 
  17. db = client[MONGO_DB] 
  18.  
  19. # 建立Chrome對象 
  20. browser = webdriver.Chrome() 
  21. wait = WebDriverWait(browser, 10) 
  22.  
  23. def get_taobao_cookies(): 
  24. url = 'https://www.taobao.com/' 
  25. browser.get('https://login.taobao.com/') 
  26. while True: 
  27. print("please login to Taobao!") 
  28. time.sleep(4) 
  29. while browser.current_url == url: 
  30. tbCookies = browser.get_cookies() 
  31. browser.quit() 
  32. output_path = open('taobaoCookies.pickle', 'wb') 
  33. pickle.dump(tbCookies, output_path) 
  34. output_path.close() 
  35. return tbCookies 
  36.  
  37. def read_taobao_cookies(): 
  38. if os.path.exists('taobaoCookies.pickle'): 
  39. read_path = open('taobaoCookies.pickle', 'rb') 
  40. tbCookies = pickle.load(read_path) 
  41. else: 
  42. tbCookies = get_taobao_cookies() 
  43. return tbCookies 
  44.  
  45. def search(): 
  46. try: 
  47. # 直接調用get()方法不行了,淘寶有反爬蟲機制,因此要先傳一個cookies進去 
  48. # browser.get('https://www.taobao.com') 
  49. cookies = read_taobao_cookies() 
  50. # add_cookie以前要先打開一下網頁,否則他媽的會報invalid domain錯誤. 日了狗了 
  51. browser.get('https://www.taobao.com') 
  52. for cookie in cookies: 
  53. # stackoverflow查到的,不知道爲啥,要把expiry這個鍵值對刪掉,否則的話,會報invalid argument,MD! 
  54. if 'expiry' in cookie: 
  55. del cookie['expiry'] 
  56. browser.add_cookie(cookie) 
  57. browser.get('https://www.taobao.com') 
  58. input_text = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '#q'))) 
  59. submit = wait.until( 
  60. EC.element_to_be_clickable((By.CSS_SELECTOR, '#J_TSearchForm > div.search-button > button'))) 
  61. input_text.send_keys(KEYWORD) 
  62. submit.click() 
  63. total = wait.until( 
  64. EC.presence_of_element_located((By.CSS_SELECTOR, '#mainsrp-pager > div > div > div > div.total'))) 
  65. get_products() 
  66. return total.text 
  67. except TimeoutException: 
  68. # 注意這是個遞歸,若是超時的話,就再請求一次 
  69. return search() 
  70.  
  71. def next_page(page_number): 
  72. try: 
  73. input_text = wait.until( 
  74. EC.presence_of_element_located((By.CSS_SELECTOR, '#mainsrp-pager > div > div > div > div.form > input'))) 
  75. submit = wait.until(EC.element_to_be_clickable( 
  76. (By.CSS_SELECTOR, '#mainsrp-pager > div > div > div > div.form > span.btn.J_Submit'))) 
  77. input_text.clear() 
  78. input_text.send_keys(page_number) 
  79. submit.click() 
  80. wait.until(EC.text_to_be_present_in_element( 
  81. (By.CSS_SELECTOR, '#mainsrp-pager > div > div > div > ul > li.item.active > span'), str(page_number))) 
  82. get_products() 
  83. except TimeoutException: 
  84. return next_page(page_number) 
  85.  
  86. def get_products(): 
  87. wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '#mainsrp-itemlist .items .item'))) 
  88. html = browser.page_source 
  89. doc = pq(html) 
  90. items = doc('#mainsrp-itemlist .items .item').items() 
  91. for item in items: 
  92. product = { 
  93. # 不知道爲何,取src的話,會出現一些s.gif的連接,因此改取原始圖片 
  94. 'image': item.find('.pic .img').attr('data-src'), 
  95. 'price': item.find('.price').text(), 
  96. 'deal': item.find('.deal-cnt').text()[:-3], 
  97. 'title': item.find('.title').text(), 
  98. 'shop': item.find('.shop').text(), 
  99. 'location': item.find('.location').text() 
  100. } 
  101. save_to_mongo(product) 
  102.  
  103. def save_to_mongo(result): 
  104. try: 
  105. if db[MONGO_TABLE].insert(result): 
  106. print('存儲到MONGODB成功:',result) 
  107. except Exception: 
  108. print('存儲到MONGODB失敗',result) 
  109.  
  110. def main(): 
  111. try: 
  112. total = search() 
  113. total = int(re.compile('(\d+)').search(total).group(1)) 
  114. for i in range(2, total + 1): 
  115. next_page(i) 
  116. except Exception as exp: 
  117. print('出錯啦',exp) 
  118. finally: 
  119. browser.close() 
  120.  
  121. if __name__ == '__main__': 
  122. main() 
  123.  

吾碼2016
吾碼2016
相關文章
相關標籤/搜索