使用selenium和pyquery爬取京东商品列表过程解析


Posted in Python onAugust 15, 2019

今天一起学起使用selenium和pyquery爬取京东的商品列表。本文的所有代码是在pycharm IDE中完成的,操作系统window 10。

1、准备工作

安装pyquery和selenium类库。依次点击file->settings,会弹出如下的界面:

使用selenium和pyquery爬取京东商品列表过程解析

然后依次点击:project->project Interpreter->"+",,如上图的红色框所示。然后会弹出下面的界面:

使用selenium和pyquery爬取京东商品列表过程解析

输入selenium,在结果列表中选中“selenium”,点击“install package”按钮安装selenium类库。pyquery也是一样的安装方法。

安装chrome和chrome driver插件。chrome dirver插件下载地址:http://npm.taobao.org/mirrors/chromedriver/。 切记chrome和chrome dirver的版本要一致。我的chrome版本是70,对应chrome driver是2.44,2.43,2.42。

下载chrome driver解压后,将exe文件拷贝到pycharm开发空间的Scripts文件夹中:

使用selenium和pyquery爬取京东商品列表过程解析

2、分析要爬取的页面

这次是爬取京东图书中计算机书籍类书籍的信息。

打开chrome,打开开发者工具,输入www.jd.com,分析查询输入框和查询按钮的css代码:

使用selenium和pyquery爬取京东商品列表过程解析

通过分析发现,搜索框的css代码是id=“key”,查询按钮的css代码是class=“button”。下面是使用selenium调用chrome浏览器在搜索框输入关键词“计算机书籍”并点击查询按钮出发查询请求的代码:

from selenium import webdriver
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from pyquery import PyQuery as pq

#通过Chrome()方法打开chrome浏览器
browser = webdriver.Chrome()
#访问京东网站
browser.get("https://www.jd.com")
#等待50秒
wait = WebDriverWait(browser, 50)
#通过css选择器的id属性获得输入框
input = browser.find_element_by_id('key')
#在输入框中写入要查询的信息
input.send_keys('计算机书籍')
#获取查询按钮
submit_button = browser.find_element_by_class_name('button')
#点击查询按钮
submit_button.click()

上面代码成功启动chrome浏览器,自动完成在搜索框中输入关键词并点击查询按钮的操作。

点击完查询按钮之后,会加载出符合条件的书籍,如下图所示:

使用selenium和pyquery爬取京东商品列表过程解析

鼠标往下滚动到达网页底部时,会看到分页的界面:

使用selenium和pyquery爬取京东商品列表过程解析

下一步要做的工作就是分析商品列表页和分页的css代码。

我们要爬去图书的书名、图片、价格、出版社、评价数量信息。下图是商品列表也的界面,

使用selenium和pyquery爬取京东商品列表过程解析

通过开发者工具可知class="gl-item"的li节点是一条商品的信息,上图这个的红色框。

  • 绿色框是商品的图片信息。对应的是class=“p-img”的div节点。
  • 蓝色框是商品的价格信息,对应的是class="p-price"的div节点。
  • 黑色框是商品的名称信息,对应的是class="p-name"的div节点。
  • 紫色狂是商品的评价信息,对应的是class="p-commit"的div节点。
  • 褐色框是商品的出版社信息,对应的是class=“p-shopnum”的div节点。

我们使用pyquery解析商品的信息,使用selenium打开一个页面时,通过page_source属性就可以得到页面的源码。

这里有个坑需要注意:京东的商品列表页是显示固定数量的商品,当加载新的商品页的时候,并不是一下子把该页的商品都加载出来,而是鼠标向下滚动时才会动态加载新的商品。因此我们在使用selenium时,要将鼠标设置自动滚动到商品列表页的底部,这样就会把该页的所有商品都显示出现,爬取的数据才能完整,否则会出现丢失。

下面给出解析一个商品的代码:

from selenium import webdriver
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from pyquery import PyQuery as pq
import time
#通过Chrome()方法打开chrome浏览器
browser = webdriver.Chrome()
#访问京东网站
browser.get("https://www.jd.com")
#等待50秒
wait = WebDriverWait(browser, 50)
#通过css选择器的id属性获得输入框
input = browser.find_element_by_id('key')
#在输入框中写入要查询的信息
input.send_keys('计算机书籍')
#获取查询按钮
submit_button = browser.find_element_by_class_name('button')
#点击查询按钮
submit_button.click()

# 模拟下滑到底部操作
for i in range(1, 5):
  browser.execute_script("window.scrollTo(0, document.body.scrollHeight);")
  time.sleep(1)

#商品列表的总页数
total = wait.until(
  EC.presence_of_all_elements_located(
    (By.CSS_SELECTOR, '#J_bottomPage > span.p-skip > em:nth-child(1) > b')
  )
)

html = browser.page_source.replace('xmlns', 'another_attr')

doc = pq(html)
#一个商品信息是存放在class=“gl-item”的li节点内,items()方法是获取所有的商品列表。
li_list = doc('.gl-item').items()
#循环解析每个商品的信息
for item in li_list:
  image_html = item('.gl-i-wrap .p-img')
  book_img_url = item.find('img').attr('data-lazy-img')
  if book_img_url == "done":
    book_img_url = item.find('img').attr('src')
  print('图片地址:' + book_img_url)
  item('.p-name').find('font').remove()
  book_name = item('.p-name').find('em').text()
  print('书名:' + book_name)
  price = item('.p-price').find('em').text() + str(item('.p-price').find('i').text())
  print('价格:' + price)
  commit = item('.p-commit').find('strong').text()
  print('评价数量:' + commit)
  shopnum = item('.p-shopnum').find('a').text()
  print('出版社:' + shopnum)
  print('++++++++++++++++++++++++++++++++++++++++++++')

对于有分页的情况,需要一页一页的解析商品,我们可以通过selenium调用“下一页”按钮来获取下一页的源代码。我们来分析下一页的css代码,滚动鼠标到网页的底部,会看到分页的情况:

使用selenium和pyquery爬取京东商品列表过程解析

通过上图可知,需要获取到“下一页”按钮,然后调用click方法。相应的代码为:

next_page_button = wait.until(
    EC.element_to_be_clickable((By.CSS_SELECTOR, '#J_bottomPage > span.p-num > a.pn-next > em'))
  )
  next_page_button.click()

  #滑动到页面底部,用于加载数据
  for i in range(0,3):
    browser.execute_script("window.scrollTo(0, document.body.scrollHeight);")
    time.sleep(10)

  #一页显示60个商品,"#J_goodsList > ul > li:nth-child(60)确保60个商品都正常加载出来。
  wait.until(
    EC.presence_of_all_elements_located((By.CSS_SELECTOR, "#J_goodsList > ul > li:nth-child(60)"))
  )
  # 判断翻页成功,当底部的分页界面上显示第几页时,就显示翻页成功。
  wait.until(
    EC.text_to_be_present_in_element((By.CSS_SELECTOR, "#J_bottomPage > span.p-num > a.curr"), str(page_num))
  )

下面给出完整代码:

from selenium import webdriver
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from pyquery import PyQuery as pq
import time
#打开不同的浏览器实例
def openBrower(brower_type):
  if brower_type == 'chrome':
    return webdriver.Chrome()
  elif brower_type == 'firefox':
    return webdriver.Firefox()
  elif brower_type == 'safari':
    return webdriver.Safari()
  elif brower_type == 'PhantomJS':
    return webdriver.PhantomJS()
  else :
    return webdriver.Ie()
def parse_website():
  # 通过Chrome()方法打开chrome浏览器
  browser = openBrower('chrome')
  # 访问京东网站
  browser.get("https://www.jd.com")
  # 等待50秒
  wait = WebDriverWait(browser, 50)
  # 通过css选择器的id属性获得输入框。until方法表示浏览器完全加载到对应的节点,才返回相应的对象。presence_of_all_elements_located是通过css选择器加载节点
  input = wait.until(
    EC.presence_of_all_elements_located((By.CSS_SELECTOR, '#key'))
  )

  # input = browser.find_element_by_id('key')
  # 在输入框中写入要查询的信息
  input[0].send_keys('计算机书籍')
  # 查询按钮完全加载完毕,返回查询按钮对象
  submit_button = wait.until(
    EC.element_to_be_clickable((By.CSS_SELECTOR, '.button'))
  )
  # 点击查询按钮
  submit_button.click()

  # 模拟下滑到底部操作
  for i in range(0,3):
    browser.execute_script("window.scrollTo(0, document.body.scrollHeight);")
    time.sleep(3)

  # 商品列表的总页数
  total = wait.until(
    EC.presence_of_all_elements_located(
      (By.CSS_SELECTOR, '#J_bottomPage > span.p-skip > em:nth-child(1) > b')
    )
  )
  html = browser.page_source.replace('xmlns', 'another_attr')
  parse_book(1,html)

  for page_num in range(2,int(total[0].text) + 1):
    parse_next_page(page_num,browser,wait)

##解析下一页
def parse_next_page(page_num,browser,wait):

  next_page_button = wait.until(
    EC.element_to_be_clickable((By.CSS_SELECTOR, '#J_bottomPage > span.p-num > a.pn-next > em'))
  )
  next_page_button.click()

  #滑动到页面底部,用于加载数据
  for i in range(0,3):
    browser.execute_script("window.scrollTo(0, document.body.scrollHeight);")
    time.sleep(10)

  #一页显示60个商品,"#J_goodsList > ul > li:nth-child(60)确保60个商品都正常加载出来。
  wait.until(
    EC.presence_of_all_elements_located((By.CSS_SELECTOR, "#J_goodsList > ul > li:nth-child(60)"))
  )
  # 判断翻页成功,当底部的分页界面上显示第几页时,就显示翻页成功。
  wait.until(
    EC.text_to_be_present_in_element((By.CSS_SELECTOR, "#J_bottomPage > span.p-num > a.curr"), str(page_num))
  )

  html = browser.page_source.replace('xmlns', 'another_attr')
  parse_book(page_num, html)

def parse_book(page,html):
  doc = pq(html)
  li_list = doc('.gl-item').items()
  print('-------------------第' + str(page) + '页的图书信息---------------------')
  for item in li_list:
    image_html = item('.gl-i-wrap .p-img')
    book_img_url = item.find('img').attr('data-lazy-img')
    if book_img_url == "done":
      book_img_url = item.find('img').attr('src')
    print('图片地址:' + book_img_url)
    item('.p-name').find('font').remove()
    book_name = item('.p-name').find('em').text()
    print('书名:' + book_name)
    price = item('.p-price').find('em').text() + str(item('.p-price').find('i').text())
    print('价格:' + price)
    commit = item('.p-commit').find('strong').text()
    print('评价数量:' + commit)
    shopnum = item('.p-shopnum').find('a').text()
    print('出版社:' + shopnum)
    print('++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++')

def main():
  parse_website()
if __name__ == "__main__":
  main()

3、总结

(1)要记得调用 browser.execute_script("window.scrollTo(0, document.body.scrollHeight);")方法模拟鼠标向下滚动的操作加载数据,否则数据会不完整。

(2)在通过page_source获取网页源码时,如果有xmlns命名空间,则要将该命名空间空其他的字段代替,否则使用pyquery解析网页时,会解析不出数据。pyquery解析xmlns命名空间时,会自动隐藏掉某些属性。导致无法征程解析网页,原因不详,如果有人知道原因请告知。

(3)尽量用wait.until(EC.presence_of_all_elements_located())方法,这样可以避免网页无法正常加载而提前返回网页信息的情况。保证数据的准确。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
python利用beautifulSoup实现爬虫
Sep 29 Python
python实现文件快照加密保护的方法
Jun 30 Python
python 利用文件锁单例执行脚本的方法
Feb 19 Python
python创造虚拟环境方法总结
Mar 04 Python
python将视频转换为全字符视频
Apr 26 Python
详解基于python的多张不同宽高图片拼接成大图
Sep 26 Python
Python3离线安装Requests模块问题
Oct 13 Python
通过字符串导入 Python 模块的方法详解
Oct 27 Python
Python 3 使用Pillow生成漂亮的分形树图片
Dec 24 Python
关于多元线性回归分析——Python&SPSS
Feb 24 Python
详解Pycharm出现out of memory的终极解决方法
Mar 03 Python
python计算列表元素与乘积详情
Aug 05 Python
如何爬取通过ajax加载数据的网站
Aug 15 #Python
Python K最近邻从原理到实现的方法
Aug 15 #Python
Python数据可视化 pyecharts实现各种统计图表过程详解
Aug 15 #Python
浅谈Python 敏感词过滤的实现
Aug 15 #Python
pycharm创建scrapy项目教程及遇到的坑解析
Aug 15 #Python
通过selenium抓取某东的TT购买记录并分析趋势过程解析
Aug 15 #Python
Python依赖包整体迁移方法详解
Aug 15 #Python
You might like
在普通HTTP上安全地传输密码
2007/07/21 PHP
整理的一些实用WordPress后台MySQL操作命令
2013/01/07 PHP
JavaScript 新手24条实用建议[TUTS+]
2009/06/21 Javascript
关于JavaScript中var声明变量作用域的推断
2010/12/16 Javascript
javascript检测浏览器flash版本的实现代码
2011/12/06 Javascript
javascript修改IMG标签的src问题
2014/03/28 Javascript
JSONP跨域GET请求解决Ajax跨域访问问题
2014/12/31 Javascript
12行javascript代码绘制一个八卦图
2015/04/02 Javascript
Bootstrap每天必学之表单
2015/11/23 Javascript
学习JavaScript设计模式(策略模式)
2015/11/26 Javascript
javascript实现全角转半角的方法
2016/01/23 Javascript
javascript数组遍历的方法实例分析
2016/09/13 Javascript
使用vue实现点击按钮滑出面板的实现代码
2017/01/10 Javascript
JS实现仿百度文库评分功能
2017/01/12 Javascript
Vue form 表单提交+ajax异步请求+分页效果
2017/04/22 Javascript
详解Angular中的自定义服务Service、Provider以及Factory
2017/04/22 Javascript
Angularjs验证用户输入的字符串是否为日期时间
2017/06/01 Javascript
jQueryeasyui 中如何使用datetimebox 取两个日期间相隔的天数
2017/06/13 jQuery
jquery ajax 请求小技巧实例分析
2019/11/11 jQuery
python文件和目录操作方法大全(含实例)
2014/03/12 Python
Python实现数通设备端口使用情况监控实例
2015/07/15 Python
Python中生成器和迭代器的区别详解
2018/02/10 Python
python 重定向获取真实url的方法
2018/05/11 Python
python指定写入文件时的编码格式方法
2018/06/07 Python
python 第三方库paramiko的常用方式
2021/02/20 Python
印尼综合在线预订网站:Tiket.com(机票、酒店、火车、租车和娱乐)
2018/10/11 全球购物
Hotels.com英国:全球领先的酒店住宿提供商
2019/01/24 全球购物
一些Solaris面试题
2015/12/22 面试题
工程监理应届生求职信
2013/11/09 职场文书
求职信模板怎么做
2014/01/26 职场文书
运动会演讲稿300字
2014/08/25 职场文书
党课心得体会范文
2014/09/09 职场文书
2015年行政管理人员工作总结
2015/10/15 职场文书
2016公司中秋节寄语
2015/12/07 职场文书
详解Python描述符的工作原理
2021/06/11 Python
Python软件包安装的三种常见方法
2022/07/07 Python