今天一起学起使用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())方法,这样可以避免网页无法正常加载而提前返回网页信息的情况。保证数据的准确。

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

广告合作:本站广告合作请联系QQ:858582 申请时备注:广告合作(否则不回)
免责声明:本站资源来自互联网收集,仅供用于学习和交流,请遵循相关法律法规,本站一切资源不代表本站立场,如有侵权、后门、不妥请联系本站删除!

《魔兽世界》大逃杀!60人新游玩模式《强袭风暴》3月21日上线

暴雪近日发布了《魔兽世界》10.2.6 更新内容,新游玩模式《强袭风暴》即将于3月21 日在亚服上线,届时玩家将前往阿拉希高地展开一场 60 人大逃杀对战。

艾泽拉斯的冒险者已经征服了艾泽拉斯的大地及遥远的彼岸。他们在对抗世界上最致命的敌人时展现出过人的手腕,并且成功阻止终结宇宙等级的威胁。当他们在为即将于《魔兽世界》资料片《地心之战》中来袭的萨拉塔斯势力做战斗准备时,他们还需要在熟悉的阿拉希高地面对一个全新的敌人──那就是彼此。在《巨龙崛起》10.2.6 更新的《强袭风暴》中,玩家将会进入一个全新的海盗主题大逃杀式限时活动,其中包含极高的风险和史诗级的奖励。

《强袭风暴》不是普通的战场,作为一个独立于主游戏之外的活动,玩家可以用大逃杀的风格来体验《魔兽世界》,不分职业、不分装备(除了你在赛局中捡到的),光是技巧和战略的强弱之分就能决定出谁才是能坚持到最后的赢家。本次活动将会开放单人和双人模式,玩家在加入海盗主题的预赛大厅区域前,可以从强袭风暴角色画面新增好友。游玩游戏将可以累计名望轨迹,《巨龙崛起》和《魔兽世界:巫妖王之怒 经典版》的玩家都可以获得奖励。