站點分析
- 看了交互,好複雜
- 看了下Ajax,好複雜
- 看了下其他內容,看不懂...
所以,沒啥好分析的,直接上selenium吧
源碼及遇到的問題
在搜索時,會跳轉到登錄界面
這個沒有辦法,是淘寶的反爬蟲機制. 因爲通過selenium webdriver調用的瀏覽器會有很多異於正常瀏覽器的參數,具體生成了啥參數,咱也沒看懂.
具體的可以參考下面這個大姐的文章
而且阿里不愧是阿里,哪怕webdriver調用的chrome中輸入用戶名和密碼依舊不可以.
網上查了一下,基本是selenium是被封的死死的,基本上比較靠譜的方法就是使用pyppeteer
庫.
那麼問題來了...
- 我這次就是玩selenium的,臨陣換庫,不好.
- 懶
- 懶
- 懶
好了,總結了這麼多,最終,發現了淘寶的一個bug. 雖然用戶名密碼登錄的方式會由於ua值校驗等問題被拒絕. 但是掃碼登錄不會...
所以我的解決思路很土,先掃碼登錄,拿到cookie,然後調用chrome之前,先把cookie寫進去. (注意!這裏有個坑,很大的坑) 如果不出意外的話,應該是可以的.
step1:幹起來! 先取cookie
- def get_taobao_cookies():
- url = 'https://www.taobao.com/'
- browser.get('https://login.taobao.com/')
- while True:
- print("please login to Taobao!")
- # 這裏等一下下
- time.sleep(4)
- # 等到界面跳轉到首頁之後,下手
- while browser.current_url == url:
- tbCookies = browser.get_cookies()
- browser.quit()
- output_path = open('taobaoCookies.pickle', 'wb')
- pickle.dump(tbCookies, output_path)
- output_path.close()
- return tbCookies
-
知識補充: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
這個很簡單,不做過多描述
- def read_taobao_cookies():
- if os.path.exists('taobaoCookies.pickle'):
- read_path = open('taobaoCookies.pickle', 'rb')
- tbCookies = pickle.load(read_path)
- else:
- tbCookies = get_taobao_cookies()
- return tbCookies
-
第二部分:講cookie載入chrome
這個可把我坑慘了.
先看代碼,在search()方法中定義瞭如何載入cookie
- cookies = read_taobao_cookies()
- # add_cookie之前要先打開一下網頁,不然他媽的會報invalid domain錯誤. 日了狗了
- browser.get('https://www.taobao.com')
- for cookie in cookies:
- # stackoverflow查到的,不知道爲啥,要把expiry這個鍵值對刪掉,不然的話,會報invalid argument,MD!
- if 'expiry' in cookie:
- del cookie['expiry']
- browser.add_cookie(cookie)
-
這裏需要注意的有兩點:
- 在調用add_cookie()方法之前,必須先打開一個網頁.不然的話就會報
InvalidCookieDomainException
的錯誤. - cookie中的'expiry'屬性要刪除,不然會報
invalid argument: invalid 'expiry'
但是看了下API,add_cookie()是支持這個expiry這個參數的
後來查了一下,當前chromedriver對於expiry只支持int64,不支持double. 據說是chromedriver的一個bug,在後續版本中會修復.
詳細回答參見這個問題下的高票答案
step3:放飛自我
這兩個問題解決了之後,基本上剩下的都不是什麼大問題了. 這裏說一個之前不知道的小技巧,chrome瀏覽器在源碼審查的時候,可以選中頁面元素,直接右鍵複製CSS選擇器
這個功能還挺好使的. 表示之前並不知道...
關於phantomJS瀏覽器的問題
在使用selenium的時候,如果不想看到瀏覽器界面,可是使用 phantomJS這個無界面的瀏覽器來代替. 但是看到pycharm報了個warning. 說是phantomJS已經被depressed. 建議使用headless chrome替代.
於是看了一眼headless chrome怎麼用. 很簡單,在調用chrome的時候傳入一個參數即可.
- chrome_options = Options()
- chrome_options.add_argument('--headless')
- browser = webdriver.Chrome(options=chrome_options)
-
源碼
- import os
- import pickle
- import re
- import time
-
- from pyquery import PyQuery as pq
- 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.ui import WebDriverWait
- import pymongo
- from config import *
-
- #連接數據庫
- client = pymongo.MongoClient(MONGO_URL)
- db = client[MONGO_DB]
-
- # 創建Chrome對象
- browser = webdriver.Chrome()
- wait = WebDriverWait(browser, 10)
-
- def get_taobao_cookies():
- url = 'https://www.taobao.com/'
- browser.get('https://login.taobao.com/')
- while True:
- print("please login to Taobao!")
- time.sleep(4)
- while browser.current_url == url:
- tbCookies = browser.get_cookies()
- browser.quit()
- output_path = open('taobaoCookies.pickle', 'wb')
- pickle.dump(tbCookies, output_path)
- output_path.close()
- return tbCookies
-
- def read_taobao_cookies():
- if os.path.exists('taobaoCookies.pickle'):
- read_path = open('taobaoCookies.pickle', 'rb')
- tbCookies = pickle.load(read_path)
- else:
- tbCookies = get_taobao_cookies()
- return tbCookies
-
- def search():
- try:
- # 直接調用get()方法不行了,淘寶有反爬蟲機制,所以要先傳一個cookies進去
- # browser.get('https://www.taobao.com')
- cookies = read_taobao_cookies()
- # add_cookie之前要先打開一下網頁,不然他媽的會報invalid domain錯誤. 日了狗了
- browser.get('https://www.taobao.com')
- for cookie in cookies:
- # stackoverflow查到的,不知道爲啥,要把expiry這個鍵值對刪掉,不然的話,會報invalid argument,MD!
- if 'expiry' in cookie:
- del cookie['expiry']
- browser.add_cookie(cookie)
- browser.get('https://www.taobao.com')
- input_text = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '#q')))
- submit = wait.until(
- EC.element_to_be_clickable((By.CSS_SELECTOR, '#J_TSearchForm > div.search-button > button')))
- input_text.send_keys(KEYWORD)
- submit.click()
- total = wait.until(
- EC.presence_of_element_located((By.CSS_SELECTOR, '#mainsrp-pager > div > div > div > div.total')))
- get_products()
- return total.text
- except TimeoutException:
- # 注意這是個遞歸,如果超時的話,就再請求一次
- return search()
-
- def next_page(page_number):
- try:
- input_text = wait.until(
- EC.presence_of_element_located((By.CSS_SELECTOR, '#mainsrp-pager > div > div > div > div.form > input')))
- submit = wait.until(EC.element_to_be_clickable(
- (By.CSS_SELECTOR, '#mainsrp-pager > div > div > div > div.form > span.btn.J_Submit')))
- input_text.clear()
- input_text.send_keys(page_number)
- submit.click()
- wait.until(EC.text_to_be_present_in_element(
- (By.CSS_SELECTOR, '#mainsrp-pager > div > div > div > ul > li.item.active > span'), str(page_number)))
- get_products()
- except TimeoutException:
- return next_page(page_number)
-
- def get_products():
- wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '#mainsrp-itemlist .items .item')))
- html = browser.page_source
- doc = pq(html)
- items = doc('#mainsrp-itemlist .items .item').items()
- for item in items:
- product = {
- # 不知道爲什麼,取src的話,會出現一些s.gif的鏈接,所以改取原始圖片
- 'image': 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()
- }
- save_to_mongo(product)
-
- def save_to_mongo(result):
- try:
- if db[MONGO_TABLE].insert(result):
- print('存儲到MONGODB成功:',result)
- except Exception:
- print('存儲到MONGODB失敗',result)
-
- def main():
- try:
- total = search()
- total = int(re.compile('(\d+)').search(total).group(1))
- for i in range(2, total + 1):
- next_page(i)
- except Exception as exp:
- print('出錯啦',exp)
- finally:
- browser.close()
-
- if __name__ == '__main__':
- main()
-