一、前言


俗话说,上班时间是公司的,下班了时间才是自己的。搞点事情,写个爬虫程序,每天定期爬取点段子,看着自己爬的段子,也是一种乐趣。

二、Python爬取嗅事百科段子

1.确定爬取的目标网页

首先我们要明确目标,本次爬取的是糗事百科文字模块的段子。

(糗事百科)->分析目标(策略:url格式(范围)、数据格式、网页编码)->编写代码->执行爬虫

2.分析爬取的目标网页

段子链接:https://www.qiushibaike.com/text/

访问链接可以看到如下的页面,一个红框代表一个段子内容,也就是对应html源码的一个div浮层。页面布局采用分页的方式,每页显示25条,总共13页。点击页码或者"下一页"会跳转到相应页面。Chrome浏览器F12可以看到,每页内容都是同步加载的,而且请求次数较多,显然不能采用直接模拟请求的方式,这里采用的爬取策略是Python Selenium,每获取和解析完一页的段子,点击 "下一页" 跳转到对应页码页继续解析,直至解析并记录所有的段子。

Chrome F12查看Network模块,看到请求密密麻麻的,下载各种document、script js脚本、stylesheet样式,图片jpeg、png等。

有个情况需要注意,当一个段子内容字数太多,会被截断,出现省略号“...”和"查看全文"的跳转链接,为了获取完整的段子信息,需要增加多一个步骤,请求段子的链接,再截取里面的全部内容。

 3.编写代码

下载网页内容,我使用python requests第三方库,发起GET请求方式。

  1. def do_get_request(self, url, headers=None, timeout=3, is_return_text=True, num_retries=2):
  2. if url is None:
  3. return None
  4. print('Downloading:', url)
  5. if headers is None: # 默认请求头
  6. headers = {
  7. 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36'}
  8. response = None
  9. try:
  10. response = requests.get(url, headers=headers, timeout=timeout)
  11.  
  12. response.raise_for_status() # a 4XX client error or 5XX server error response,raise requests.exceptions.HTTPError
  13. if response.status_code == requests.codes.ok:
  14. if is_return_text:
  15. html = response.text
  16. else:
  17. html = response.json()
  18. else:
  19. html = None
  20. except requests.Timeout as err:
  21. print('Downloading Timeout:', err.args)
  22. html = None
  23. except requests.HTTPError as err:
  24. print('Downloading HTTP Error,msg:{0}'.format(err.args))
  25. html = None
  26. if num_retries > 0:
  27. if 500 <= response.status_code < 600:
  28. return self.do_get_request(url, headers=headers, num_retries=num_retries - 1) # 服务器错误,导致请求失败,默认重试2次
  29. except requests.ConnectionError as err:
  30. print('Downloading Connection Error:', err.args)
  31. html = None
  32.  
  33. return html

解析网页内容,我使用python lxml第三方库和python的re标准库,在解析之前,使用lxml的lxml的Cleaner清理器,把多余的空行、注释,脚本和样式等清楚,再通过re.sub(pattern, string, flags)替换一些标签和字符串<br>和\n空行符,这样才能保证lxml.html.

_Element的text属性能够获取完整的段子内容(如果段子中有<br>,text属性只会取<br>前部分的文字)。cssselect.CSSSelector()通过CSS选择器#content-left获取其下所有的div,再使用e.find()和e.findall()的xpath定位方式分别获取段子的投票数、评论数、

链接地址和内容文本。

  1. def duanzi_scrapter(html_doc, page_num=1):
  2. html_after_cleaner = cleaner.clean_html(html_doc)
  3. # 去除段子内容中的<br>
  4. pattern = re.compile('<br>|\n')
  5. html_after_cleaner = re.sub(pattern, '', html_after_cleaner)
  6. document = etree.fromstring(html_after_cleaner, parser)
  7. print('正在解析第%s页段子...' % str(page_num))
  8. try:
  9. sel = cssselect.CSSSelector('#content-left > div')
  10. for e in sel(document):
  11.  
  12. try:
  13. # a content 获取段子信息
  14. a = e.find('.//a[@class="contentHerf"]')
  15. a_href = a.attrib['href'] # 格式/article/105323928
  16. spans = e.findall('.//a[@class="contentHerf"]/div/span')
  17. if len(spans) > 1: # 出现“查看全文”
  18. urls.add_new_url(a_href) # 保存段子链接
  19. else:
  20. duanzi_info = {}
  21. duanzi_info['dz_url'] = 'https://www.qiushibaike.com' + a_href # 段子链接地址
  22. duanzi_info['dzContent'] = spans[0].text # 段子内容
  23.  
  24. # div stats
  25. spans = e.findall('.//div[@class="stats"]/span')
  26. for span in spans:
  27. i = span.find('.//i')
  28. if span.get('class') == 'stats-vote':
  29. duanzi_info['vote_num'] = i.text # 投票数
  30. elif span.get('class') == 'stats-comments': # 评论数
  31. duanzi_info['comment_num'] = i.text
  32. collect_data(duanzi_info)
  33.  
  34. except Exception as err:
  35. print('提取段子异常,进入下一循环')
  36. continue
  37. print('解析第%s页段子结束' % str(page_num))
  38. next_page(page_num + 1) # 进入下一页
  39. except TimeoutException as err:
  40. print('解析网页出错:', err.args)
  41. return next_page(page_num + 1) # 捕获异常,直接进入下一页

下载并解析因字数过长截断而无法获取完整段子内容的段子链接页面,获取段子的投票数、评论数、链接地址和内容文本。

模拟单击“下一页”按钮,跳转到下一页。

  1. def next_page(page_num_input):
  2. # print('当前是第%d页' % (page_num_input)) # 首页下标为0,依次累加
  3. if page_num_input > 1: # 超出最大页码,直接返回
  4. print('超过最大页码,返回')
  5. return
  6. try:
  7. # 定位并单击"下一页",跳转到下一页
  8. submit = wait.until(
  9. EC.element_to_be_clickable((By.XPATH, '//*[@id="content-left"]/ul/li/a/span[@class="next"]'))
  10. )
  11. submit.click()
  12. time.sleep(5) # 注意:等待页面加载完成
  13. # 定位当前页码
  14. # current = wait.until(
  15. # EC.presence_of_element_located((By.XPATH, '//*[@id="content-left"]/ul/li/span[@class="current"]'))
  16. # )
  17. # print('当前页码是%s' % current.text) # 打印当前页码
  18.  
  19. html = browser.page_source
  20. duanzi_scrapter(html, page_num_input) # 解析段子
  21. except TimeoutException as err:
  22. print('翻页出错:', err.args)

保存段子信息到Excel文件

  1. from openpyxl import Workbook
  2.  
  3. class excelManager:
  4. def __init__(self, excel_name):
  5. self.workBook = Workbook()
  6. self.workSheet = self.workBook.create_sheet('duanzi')
  7. self.workSheet.append(['投票数', '评论数', '链接地址', '段子内容'])
  8. self.excelName = excel_name
  9.  
  10. def write_to_excel(self, content):
  11. try:
  12. for row in content:
  13. self.workSheet.append([row['vote_num'], row['comment_num'], row['dz_url'], row['dzContent']])
  14. self.workBook.save(self.excelName) # 保存段子信息到Excel文件
  15. except Exception as arr:
  16. print('write to excel error', arr.args)
  17.  
  18. def close_excel(self):
  19. self.workBook.close()

4.执行爬虫

爬虫跑起来了,一页一页地去爬取段子信息,并保存到集合中,最后通过get_duanzi_info()方法获取段子的投票数、评论数,链接地址和内容并保存到Excel文件中。

爬虫程序运行截图:

 Excel文件截图:

三、Python爬取嗅事百科段子总结


  糗事百科页面加载没有采用ajax的异步方式,选择使用Python Selenium方式是比较合理的。一页一页地往下排,爬完一页再下一页,直至爬完最大的页数。这次页面解析全部采用Python lxml方式,解析性能方面lxml较BeautifulSoup高,可是感觉使用没有像BeautifulSoup简单易用,还有通过xpath定位元素的时候花了很多时间,也暴露出自己对xpath定位方式不太熟悉,后期需要多花点时间。爬取效率方面,这里采用的是单进程的方式,后期版本想采用多线程的方式,在加快爬取效率的同时,会多学习些网站防爬虫的知识,包括浏览器代理,HTTP请求头,同一域名访问时间间隔等。

四、后语


  最后要庆祝下,毕竟成功把糗事百科的段子爬取下来了。本次能够成功爬取段子,Selenium PhantomJS,lxml和requests功不可没,通过本次实战,我对lxml的html元素定位和lxml API有更加深入的理解,后续会更加深入学习。期待下次实战。

Python爬虫实战三之爬取嗅事百科段子的更多相关文章

  1. 转 Python爬虫实战一之爬取糗事百科段子

    静觅 » Python爬虫实战一之爬取糗事百科段子 首先,糗事百科大家都听说过吧?糗友们发的搞笑的段子一抓一大把,这次我们尝试一下用爬虫把他们抓取下来. 友情提示 糗事百科在前一段时间进行了改版,导致 ...

  2. Python爬虫实战一之爬取糗事百科段子

    大家好,前面入门已经说了那么多基础知识了,下面我们做几个实战项目来挑战一下吧.那么这次为大家带来,Python爬取糗事百科的小段子的例子. 首先,糗事百科大家都听说过吧?糗友们发的搞笑的段子一抓一大把 ...

  3. 8.Python爬虫实战一之爬取糗事百科段子

    大家好,前面入门已经说了那么多基础知识了,下面我们做几个实战项目来挑战一下吧.那么这次为大家带来,Python爬取糗事百科的小段子的例子. 首先,糗事百科大家都听说过吧?糗友们发的搞笑的段子一抓一大把 ...

  4. python爬虫29 | 使用scrapy爬取糗事百科的例子,告诉你它有多厉害!

    是时候给你说说 爬虫框架了 使用框架来爬取数据 会节省我们更多时间 很快就能抓取到我们想要抓取的内容 框架集合了许多操作 比如请求,数据解析,存储等等 都可以由框架完成 有些小伙伴就要问了 你他妈的 ...

  5. Python爬虫实战二之爬取百度贴吧帖子

    大家好,上次我们实验了爬取了糗事百科的段子,那么这次我们来尝试一下爬取百度贴吧的帖子.与上一篇不同的是,这次我们需要用到文件的相关操作. 前言 亲爱的们,教程比较旧了,百度贴吧页面可能改版,可能代码不 ...

  6. 转 Python爬虫实战二之爬取百度贴吧帖子

    静觅 » Python爬虫实战二之爬取百度贴吧帖子 大家好,上次我们实验了爬取了糗事百科的段子,那么这次我们来尝试一下爬取百度贴吧的帖子.与上一篇不同的是,这次我们需要用到文件的相关操作. 本篇目标 ...

  7. python学习(十六)写爬虫爬取糗事百科段子

    原文链接:爬取糗事百科段子 利用前面学到的文件.正则表达式.urllib的知识,综合运用,爬取糗事百科的段子先用urllib库获取糗事百科热帖第一页的数据.并打开文件进行保存,正好可以熟悉一下之前学过 ...

  8. [爬虫]用python的requests模块爬取糗事百科段子

    虽然Python的标准库中 urllib2 模块已经包含了平常我们使用的大多数功能,但是它的 API 使用起来让人感觉不太好,而 Requests 自称 “HTTP for Humans”,说明使用更 ...

  9. 初识python 之 爬虫:使用正则表达式爬取“糗事百科 - 文字版”网页数据

    初识python 之 爬虫:使用正则表达式爬取"古诗文"网页数据 的兄弟篇. 详细代码如下: #!/user/bin env python # author:Simple-Sir ...

随机推荐

  1. 解决IOS微信浏览器底部会出现向前向后返回按钮,返回不刷新的问题

    <script type="text/javascript"> //解决IOS返回页面不刷新的问题 var isPageHide = false; window.add ...

  2. java 枚举2

    package com.wsy.test; public enum Color { RED("红色",1),GREEN("绿色",2),BLUE("蓝 ...

  3. ftm国际化解决方案

    记录一下踩过的坑,在使用ftm:message的时候我发现这个的国际化是依赖于本地浏览器的语言环境的!关于自主设置这个语言的方法有如下3种:(个人建议使用第二种,可以更加灵活且有效!第一种我这边没有生 ...

  4. 【WC2019】数树 树形DP 多项式exp

    题目大意 有两棵 \(n\) 个点的树 \(T_1\) 和 \(T_2\). 你要给每个点一个权值吗,要求每个点的权值为 \([1,y]\) 内的整数. 对于一条同时出现在两棵树上的边,这条边的两个端 ...

  5. JSSDK调用微信原生的功能上传图片保存到自己的服务器中

    第一件事首先是微信的选择图片功能,就是微信发朋友圈选择图片的时候那个界面 //调用微信拍照功能 wx.chooseImage({ count: 1, // 默认9 sizeType: ['origin ...

  6. Docke--利用 commit 理解构建镜像

    Docker 利用commit理解构建镜像 镜像是容器的基础,每次执行 docker run 的时候都会指定哪个镜像作为容器运行的基础.当我们使用Docker Hub的镜像无法满足我们的需求时,我们就 ...

  7. 【一本通1329:【例8.2】细胞&&洛谷P1451 求细胞数量】

    1329:[例8.2]细胞 [题目描述] 一矩形阵列由数字0到9组成,数字1到9代表细胞,细胞的定义为沿细胞数字上下左右还是细胞数字则为同一细胞,求给定矩形阵列的细胞个数.如: 阵列 4 10 023 ...

  8. 第一篇-生成可运行得exe文件

    1. 项目 --> 属性 2. 配置 -->Release 3. 如果可以在其他电脑运行可以不进行下面的,如果不能运行,选择代码生成-->运行库-->MT 4. 完成上述步骤后 ...

  9. virtualbox+ubuntu

    https://jingyan.baidu.com/article/7f766daff541cd4101e1d0cd.html ubuntu 安装 这台计算机似乎没有安装操作系统 待解决 注意ubun ...

  10. org.apache.catalina.core.StandardContext.startInternal Context [/test] startup failed due to previou

    解决方法: WEB-INF/classes目录下新建一个文件叫logging.properties,截图如下: 代码如下: handlers=org.apache.juli.FileHandler,j ...