博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
【一起学爬虫】爬虫实战:爬取京东零食
阅读量:6463 次
发布时间:2019-06-23

本文共 8916 字,大约阅读时间需要 29 分钟。

简介

使用Selenium+chrome/PhantomJS爬取京东零食。 京东的页面比较复杂:含有各种请求参数、加密参数,如果直接请求或者分享Ajax的话会非常的繁琐,Selenium是一个自动化测试工具,可以驱动浏览器完成各种操作:模拟点击、输入、下滑等各种功能,如此一来,我们只需要关心操作,而不需要关心后台发生了什么样的请求。PhantomJS是无界面的浏览器,比Selenium方便,phantomJS已经被废弃了,直接配置Selenium的浏览器options就可以实现无界面浏览器(详情请看文末的实现代码)。

本次实战爬取的是京东搜索“零食”出现的所有内容,解析库是PyQuery。 ###目标站点分析 分析发现京东搜索“零食”后发起了很多请求,比较复杂。这里使用Selenium驱动浏览器,这可以屏蔽这些复杂请求的分析,所以需要依次实现:

  • 模拟京东搜索关键词“美食”
  • 模拟鼠标点击
  • 获取首页的内容
  • 模拟点击翻页或者通过模拟输入翻页
  • 网页解析

Selenium和webdriver安装可看下 (1)实现步骤

浏览器右键->检查,上图中的copy选项卡可以在元素查找时使用,这篇文章中我们使用css选择器查找元素,点击“copy selector”,在查找元素时直接粘贴就可以了。 在正式写代码前需要考虑到:

  • 我们必须要等待输入框和搜索按钮加载完,才能进行输入和搜索操作,这就意味着我们在实现代码中需要加一些等待条件,这个可以上Selenium官网上查看:主要是wait_until函数。
  • 加载完后需要获取总页数等一些信息
  • 页面切换:可以点击下一页;也可以通过输入框输入页码
  • 页面切换之后,通过高亮的页码是否加载完成来判断翻页是否完成
  • 完成等待页面加载完成和翻页等之类框架的功能后,我们再考虑细节,即网页内容解析

(2)网页解析

上图可以和清楚的发现,所有页面商品都是以li的形式组织的,页面解析思路也就很明确了。 应该注意的是,在进行网页源码解析之前,我们需要先判断网页是否加载完成了。确认网页加载完后,我们获取网页的源代码,使用PyQuery进行解析(也可以使用正则表达式,上一次实战使用的是正则,这次就用PyQuery解析)。

(3)在真实贴代码和数据之前,先说明几个曾经踩过的坑:

  • 确定要爬取的数据:确定目标
  • 写代码前 一定要分析完网页源码
  • 注意细节:有些网页数据要等待一定时间后才会加载出来,网页源码中包括很多数据,其中大部分数据是通过js请求获取后才会填充到网页源码中,所以有些数据加载的比较慢,所以获取数据前一定要等待数据加载完
  • 本次爬取的是京东搜索“零食”关键词后按照评论数量进行排序的数据:商品名字、价格、总评论数、好评数、中评数、差评数、好评率等数据,分析后发现:后面四个数据,必须点击商品详情后才可以获取到,但是不同商品的详情页面有些许不同,下面是两种典型的商品详情页面: 1.五个导航栏
    2.三个导航栏
    显然上面两类商品的详情页面 不论是在五个导航栏还是三个,我们都需要点击“商品评价”按钮来获取上述后四个数据。但是两类页面明显存在差别,如果页面种类很多,可以考虑换种实现方式,如果页面种类不多,可以采用分类的方式。在本次实现中由于只存在两种页面,我们采用的是分类的方式。
  • 有些数据不会加载出来,必须下拉页面后那部分数据才会加载出来,这是下拉页面就很重要,否则会出现找到不到元素的异常
  • 为了加快网页的访问速度,可以进行必要的浏览器配置:比如,访问时不加载图片,和使用无界面浏览器等

源码和数据:

下面是爬取的数据:

源码 下面是本文的核心代码,扫描下方二维码,发送关键词“
京东”即可获取本文的完整源码和详细程序注释
公众号专注:
互联网求职面经
java
python
爬虫
大数据等技术、海量
资料分享:公众号后台回复“csdn文库下载”即可免费领取【csdn】和【百度文库】下载服务;公众号后台回复“
资料”:即可领取
5T精品学习资料
java面试考点
java面经总结,以及
几十个java、大数据项目
资料很全,你想找的几乎都有

完整源码和代码详细解析

# 京东首页搜素框def search(key_words='零食'):    try:        input_text = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '#key')))        button = wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, '#search > div > div.form > button')))        input_text.send_keys(key_words)        button.click()        total = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '#J_topPage > span > i')))        total = int(total.text)        order = wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, '#J_filter > div.f-line.top > div.f-sort > a:nth-child(3)')))        order.click()    except TimeoutException:        print('search 超时')    print('一共', total, '页')    return total# 翻页def next_page(next_page):    # 翻页使用的策略是手动输入页码,然后点击跳转,但是有个问题就是:    # 不滑动滑块直接定位页码输入框会出现:找不到该元素的异常,这主要是,页面未加载造成的    # 也就是:当你在京东上搜索商品的时候,浏览器没有下拉到翻页那里时,页面就不会加载,页面没有加载自然找不到对应的元素    # 这也是下面这三条指令的意义所在    # browser.execute_script(js)    try:        browser.execute_script('arguments[0].scrollIntoView()',                               browser.find_element_by_css_selector('#footer-2017 > div > div.copyright_info > p:nth-child(1) > a:nth-child(1)'))        time.sleep(2)        # 下滑直到“下一页”按钮出现        browser.execute_script('arguments[0].scrollIntoView()',                               browser.find_element_by_css_selector('#J_bottomPage > span.p-num > a.pn-next'))        input_text = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '#J_bottomPage > span.p-skip > input')))        jump = wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, '#J_bottomPage > span.p-skip > a')))    except TimeoutException:        print('翻页超时')    input_text.clear()    input_text.send_keys(next_page)    jump.click()    try:        # 等待翻页完成        wait.until(EC.text_to_be_present_in_element((By.CSS_SELECTOR, '#J_bottomPage > span.p-num > a.curr'), str(next_page)))        browser.execute_script('arguments[0].scrollIntoView()',                               browser.find_element_by_css_selector('#J_bottomPage > span.p-num > a.pn-next'))    except TimeoutException:        print('翻页失败', next_page)    print('\n')    print('解析第', next_page, '页商品')    try:        for good in parse_page():            write_to_csv_file(good.values())    except TimeoutException:        print('第', next_page, '页的第', count_item, '个商品解析时发生超时')        browser.switch_to.window(browser.window_handles[0])        return None# 页面数据解析def parse_page():    global count_item    count_item = 0# 统计每一页的商品    # 解析页面前,先将浏览器页面下滑置“下一页”地方    browser.execute_script('arguments[0].scrollIntoView()',                           browser.find_element_by_css_selector('#J_bottomPage > span.p-num > a.pn-next'))    wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '.m-list .ml-wrap #J_goodsList .gl-item')))    doc = pq(browser.page_source, parser="html")    goods = doc('.m-list .ml-wrap #J_goodsList .gl-item').items()    browser.switch_to.window(browser.window_handles[1])    for good in goods:        count_item = count_item +1        print('当前是第',count_item,'个商品')        detail_herf =  good.find('.p-name a').attr('href')        browser.get('http://'+detail_herf)        # 等待商品介绍 评价等数据加载完成        browser.execute_script('arguments[0].scrollIntoView()',                               browser.find_element_by_css_selector('.detail #detail .tab-main ul li'))        detail_doc = pq(browser.page_source,parser='html')        # 京东上主要有两类商品:        # 一类:与商品介绍并列的一共5个标签        # 第二类:只有商品介绍和评价两类标签,        # 下面这个css定位很容易写错        ul = list(detail_doc('.detail #detail .tab-main ul li').items())        if len(ul)==5 :            # 商品评价按钮 五个li标签,对应第二类商品            #等待评价按钮加载完成            comments_button = wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR,'#detail > div.tab-main.large.detail-elevator > ul > li:nth-child(2)')))            comments_button.click()  # 点击“评价”按钮            rate = wait.until(EC.presence_of_element_located(                (By.CSS_SELECTOR, '#i-comment > div.rate > strong')))# 等待rate加载完成            rate_comments = rate.text  # rate是webElement类型,调用text即可获取对应的文本,注意不是调用text()            # 等待差评数量加载完成(差评数量加载完了也就意味着好评和中评数量加载完了)            wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '#comments-list > div.mt > div > ul > li:nth-child(4) > a > em')))            tmp_doc = pq(browser.page_source, parser='html')            comments_obtain = tmp_doc('#comments-list .mt-inner ul li').items()# 获取评论数量            index = 0            for comment in comments_obtain:                if index == 1:                    good_comments = comment('a em').text()[1:-1]                elif index == 2:                    neutral_comments = comment('a em').text()[1:-1]                elif index == 3:                    bad_comments = comment('a em').text()[1:-1]                    break                index = index + 1            yield {                'name': good.find('.p-name a em').text().strip().replace('\n', ' '),                'price': good.find('.p-price i').text(),                'total_comments': good.find('.p-commit a').text(),                'good_comments': good_comments,                'neutral_comments': neutral_comments,                'bad_comments': bad_comments,                'good_rate': rate_comments            }        elif len(ul)==7 :# 对应第二类商品            comments_button = wait.until(                EC.element_to_be_clickable((By.CSS_SELECTOR, '#detail > div.tab-main.large > ul > li:nth-child(5)')))            comments_button.click()#点击“评价”按钮            rate = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR,'#comment > div.mc > div.comment-info.J-comment-info > div.comment-percent > div')))            rate_comments = rate.text# rate是webElement类型,调用text即可获取对应的文本,注意不是调用text()            # 等待差评数量加载完成(差评数量加载完了也就意味着好评和中评数量加载完了)            wait.until(EC.presence_of_element_located((By.CSS_SELECTOR,'#comment > div.mc > div.J-comments-list.comments-list.ETab > div.tab-main.small > ul > li:nth-child(7) > a > em')))            tmp_doc = pq(browser.page_source,parser='html')            comments_obtain = tmp_doc('.tab-main .filter-list li' ).items()            index = 0            for comment in comments_obtain:                if index==4:                    good_comments = comment('a em').text()[1:-1]                elif index==5:                    neutral_comments = comment('a em').text()[1:-1]                elif index==6:                    bad_comments = comment('a em').text()[1:-1]                    break                index = index + 1            yield {                'name': good.find('.p-name a em').text().strip().replace('\n', ' '),                'price': good.find('.p-price i').text(),                'total_comments': good.find('.p-commit a').text(),                'good_comments':good_comments,                'neutral_comments':neutral_comments,                'bad_comments':bad_comments,                'good_rate': rate_comments            }    browser.switch_to.window(browser.window_handles[0])复制代码

资料分享


欢迎关注个人公众号【菜鸟名企梦】,公众号专注:互联网求职面经javapython爬虫大数据等技术分享**: 公众号**菜鸟名企梦后台发送“csdn”即可免费领取【csdn】和【百度文库】下载服务; 公众号菜鸟名企梦后台发送“资料”:即可领取5T精品学习资料**、java面试考点java面经总结,以及几十个java、大数据项目资料很全,你想找的几乎都有

转载于:https://juejin.im/post/5cbd75c56fb9a0320b40d588

你可能感兴趣的文章
转载:《TypeScript 中文入门教程》 16、Symbols
查看>>
C#技术------垃圾回收机制(GC)
查看>>
漫谈并发编程(三):共享受限资源
查看>>
【转】github如何删除一个仓库
查看>>
Linux系统编程——进程调度浅析
查看>>
大数据Lambda架构
查看>>
openCV_java 图像二值化
查看>>
状态模式
查看>>
VC++获得微秒级时间的方法与技巧探讨(转)
查看>>
HDOJ-1010 Tempter of the Bone
查看>>
MySQL my.cnf参数配置优化详解
查看>>
JavaNIO基础02-缓存区基础
查看>>
日本开设无人机专业,打造无人机“人才市场”
查看>>
190行代码实现mvvm模式
查看>>
PXE部署实例
查看>>
cobbler初探------实现自动安装centos6.4
查看>>
Android Studio 2.0 preview3 BUG
查看>>
兼容几乎所有浏览器的透明背景效果
查看>>
Go语言4
查看>>
jeesite 框架搭建与配置
查看>>