效果图:
源代码:
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}' #用此方法提前标记好 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()
讲解:
一、open 方法
这个函数能打开当前目录下的文件,若没有该文件则自动创建一个新的文件,方法如下:
open(fileName+'.txt','w',encoding='utf-8')
函数返回值:fd = open(fileName+'.txt','w',encoding='utf-8')
,可用于读写和关闭文件,方法如下:
fd.write('title:' + str(item[0]) + '\n') #写入
fd.close() #关闭
二、parse 模块的 quote 方法
urllib.parse.quote(kw)
作用是将具有中文的不合理 URL 处理成合法的 URL(具体方法见源码)
三、re 模块
re 也称正则表达式,是一组特殊的字符序列,常常用于匹配字符串
下面讲讲 re 模块中的 findall 方法,以源代码举例:
re.findall(r'共有主题数<span class="red_text">(\d+)</span>个',get_page(base_url.format(page=str(0)))[0]
这段看起来很长,我就来分段讲解
base_url.format(page=str(0))
这句就是给前面的 page 赋值为字符串的 0,就是 format 的基本用法
r'共有主题数<span class="red_text">(\d+)</span>个'
其中r'xxxx'
和 (\d+)
就是正则表达式,前者代表引号内为正则表达式,后者代表整数( \d 代表一个数字,+ 代表取一或多次)
并且 (\d+)
和 \d+
的意思完全不一样,有括号代表仅匹配括号内的数字,没括号代表连着数字的其他字符也一起匹配
re.findall(xxxx,xxxx)[0]
第一个参数代表要匹配内容,第二个参数代表被匹配的字符串(一般是整个 html 内容)
函数返回 匹配成功内容 的列表,[0]
代表取列表第一个元素
再举一个例子:
titles = re.findall(r'href="/p/\d+" title="(.+?)"',html)
(.+?)
中的 . 号为匹配除换行符之外的所有字符,? 号为匹配字符零次或一次
函数返回值:findall 方法 的返回值是一个列表
四、time 模块 和 random 模块
time 模块 的 sleep 方法
time.sleep(t) #进程挂起的时间为 t 秒
random 模块 的 random 方法
random.random() #返回随机生成的一个实数,它在[0,1)范围内
五、将数据保存到文件
def save2file(fm,fd,data): #选择格式 选择文件 zip内容
if fm == 'txt':
for item in data:
fd.write('----------------------------------------\n')
fd.write('title:' + str(item[0]) + '\n') # zip解压
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)
上面代码可以当作模板来参考使用,具体我也不是很了解,用时回顾,不必深究
参考博客:
爬虫系列(三) urllib的基本使用
爬虫系列(五) re的基本使用
爬虫系列(六) 用urllib和re爬取百度贴吧