这篇文章我们将使用 urllib 和 re 模块爬取百度贴吧,并使用三种文件格式存储数据,下面先贴上最终的效果图

1、网页分析

(1)准备工作

首先我们使用 Chrome 浏览器打开 百度贴吧,在输入栏中输入关键字进行搜索,这里示例为 “计算机吧”

(2)分析 URL 规律

接下来我们开始分析网站的 URL 规律,以便于通过构造 URL 获取网站中所有网页的内容

第一页:http://tieba.baidu.com/f?kw=计算机&ie=utf-8&pn=0

第二页:http://tieba.baidu.com/f?kw=计算机&ie=utf-8&pn=50

第三页:http://tieba.baidu.com/f?kw=计算机&ie=utf-8&pn=100

...

通过观察不难发现,它的 URL 十分有规律,主要的请求参数分析如下:

  • kw:搜索的关键字,使用 URL 编码,可以通过 urllib.parse.quote() 方法实现
  • ie:字符编码的格式,其值为 utf-8
  • pn:当前页面的页码,并且以 50 为步幅增长

所以完整的 URL 可以泛化如下:http://tieba.baidu.com/f?kw={keyword}&ie=utf-8&pn={page}

核心代码如下:

import urllib.request
import urllib.parse
# 获取网页源代码
def get_page(url):
# 构造请求头部
headers = {
'USER-AGENT':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36'
}
# 构造请求对象
req = urllib.request.Request(url=url,headers=headers)
# 发送请求,得到响应
response = urllib.request.urlopen(req)
# 获得网页源代码
html = response.read().decode('utf-8')
# 返回网页源代码
return html

(3)分析内容规律

接下来我们直接使用快捷键 Ctrl+U 打开网页的源代码,认真分析每一页中我们需要抓取的数据

容易发现每一个帖子的内容都被包含在一个 <li> 标签中,我们可以使用正则表达式进行匹配,具体包括:

  • 主题名称:r'href="/p/\d+" title="(.+?)"'
  • 主题作者:r'title="主题作者: (.+?)"'
  • 链接地址:r'href="/p/(\d+)"'
  • 回复数:r'title="回复">(\d+)<'
  • 创建日期:r'title="创建时间">(.+?)<'

核心代码如下:

import re
# 解析网页源代码,提取数据
def parse_page(html):
# 主题名称
titles = re.findall(r'href="/p/\d+" title="(.+?)"',html)
# 主题作者
authods = re.findall(r'title="主题作者: (.+?)"',html)
# 链接地址
nums = re.findall(r'href="/p/(\d+)"',html)
links = ['http://tieba.baidu.com/p/'+str(num) for num in nums]
# 回复数量
focus = re.findall(r'title="回复">(\d+)',html)
# 创建时间
ctimes = re.findall(r'title="创建时间">(.+?)<',html)
# 获得结果
data = zip(titles,authods,links,focus,ctimes)
# 返回结果
return data

(4)保存数据

下面将数据保存为 txt 文件、json 文件和 csv 文件

import json
import csv
# 打开文件
def openfile(fm,fileName):
fd = None
if fm == 'txt':
fd = open(fileName+'.txt','w',encoding='utf-8')
elif fm == 'json':
fd = open(fileName+'.json','w',encoding='utf-8')
elif fm == 'csv':
fd = open(fileName+'.csv','w',encoding='utf-8',newline='')
return fd # 将数据保存到文件
def save2file(fm,fd,data):
if fm == 'txt':
for item in data:
fd.write('----------------------------------------\n')
fd.write('title:' + str(item[0]) + '\n')
fd.write('authod:' + str(item[1]) + '\n')
fd.write('link:' + str(item[2]) + '\n')
fd.write('focus:' + str(item[3]) + '\n')
fd.write('ctime:' + str(item[4]) + '\n')
if fm == 'json':
temp = ('title','authod','link','focus','ctime')
for item in data:
json.dump(dict(zip(temp,item)),fd,ensure_ascii=False)
if fm == 'csv':
writer = csv.writer(fd)
for item in data:
writer.writerow(item)

2、编码实现

完整代码如下,也很简单,还不到 100 行

import urllib.request
import urllib.parse
import re
import json
import csv
import time
import random # 获取网页源代码
def get_page(url):
headers = {
'USER-AGENT':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36'
}
req = urllib.request.Request(url=url,headers=headers)
response = urllib.request.urlopen(req)
html = response.read().decode('utf-8')
return html # 解析网页源代码,提取数据
def parse_page(html):
titles = re.findall(r'href="/p/\d+" title="(.+?)"',html)
authods = re.findall(r'title="主题作者: (.+?)"',html)
nums = re.findall(r'href="/p/(\d+)"',html)
links = ['http://tieba.baidu.com/p/'+str(num) for num in nums]
focus = re.findall(r'title="回复">(\d+)',html)
ctimes = re.findall(r'title="创建时间">(.+?)<',html)
data = zip(titles,authods,links,focus,ctimes)
return data # 打开文件
def openfile(fm,fileName):
if fm == 'txt':
return open(fileName+'.txt','w',encoding='utf-8')
elif fm == 'json':
return open(fileName+'.json','w',encoding='utf-8')
elif fm == 'csv':
return open(fileName+'.csv','w',encoding='utf-8',newline='')
else:
return None # 将数据保存到文件
def save2file(fm,fd,data):
if fm == 'txt':
for item in data:
fd.write('----------------------------------------\n')
fd.write('title:' + str(item[0]) + '\n')
fd.write('authod:' + str(item[1]) + '\n')
fd.write('link:' + str(item[2]) + '\n')
fd.write('focus:' + str(item[3]) + '\n')
fd.write('ctime:' + str(item[4]) + '\n')
if fm == 'json':
temp = ('title','authod','link','focus','ctime')
for item in data:
json.dump(dict(zip(temp,item)),fd,ensure_ascii=False)
if fm == 'csv':
writer = csv.writer(fd)
for item in data:
writer.writerow(item) # 开始爬取网页
def crawl():
kw = input('请输入主题贴吧名字:')
base_url = 'http://tieba.baidu.com/f?kw=' + urllib.parse.quote(kw) + '&ie=utf-8&pn={page}'
fm = input('请输入文件保存格式(txt、json、csv):')
while fm!='txt' and fm!='json' and fm!='csv':
fm = input('输入错误,请重新输入文件保存格式(txt、json、csv):')
fd = openfile(fm,kw)
page = 0
total_page = int(re.findall(r'共有主题数<span class="red_text">(\d+)</span>个',get_page(base_url.format(page=str(0))))[0])
print('开始爬取')
while page < total_page:
print('正在爬取第', int(page/50+1), '页.......')
html = get_page(base_url.format(page=str(page)))
data = parse_page(html)
save2file(fm,fd,data)
page += 50
time.sleep(random.random())
fd.close()
print('结束爬取') if __name__ == '__main__':
crawl()

【爬虫系列相关文章】

最新文章

  1. 为何img、input等内联元素可以设置宽、高
  2. &lt;总结&gt;delphi WebBrowser控件的使用中出现的bug
  3. js 阻止事件冒泡
  4. windows 下面安装npm
  5. linux内核分析 期中总结
  6. 【JNI】C向C++改造
  7. 一个通用的DAO模型实现增删改查
  8. if条件语句
  9. PLSQL_基础系列06_判断操作NVL / NULLIF / COALESCE / NVL2(案例)
  10. Comparing cards
  11. HDU 5742 It&#39;s All In The Mind (贪心)
  12. InfoSphere BigInsights 安装部署
  13. RabbitMQ-清空队列中(一个channel或连接中)的Unacknowledged状态的消息
  14. JS~delegate与live
  15. Intellij idea workflow 工作流插件安装
  16. 【Unity 3D】学习笔记三十五:游戏实例——摄像机切换镜头
  17. rayleighchan实现瑞利多径衰落信
  18. 关于Entity Framework的概念及搭建
  19. Linux中断 - 综述
  20. Gridview中的选择、删除、编辑、更新、取消留着备用。

热门文章

  1. UVA LIVE 7146 Defeat the Enemy
  2. Uboot中支持lcd和hdmi显示不同的logo图片【转】
  3. hdu 1213(并查集模版题)
  4. 完美解决 linux sublime 中文无法输入
  5. 442C
  6. 多线程编程和Java网络编程
  7. django自带url模板标签的使用
  8. 洛谷P2916 [USACO08NOV]为母牛欢呼(最小生成树)
  9. Jmeter_Beanshell解析并提取json响应
  10. Phoenix与Squirrel 是什么?