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
相关文章
相关标签/搜索