目标分析:
进入百度贴吧,访问: https://tieba.baidu.com/index.html
搜索权利的游戏
定义需要爬取的数据,爬取每个帖子上面的内容
定位到每个帖子,即取 li标签 里面 'class'=' j_thread_list clearfix' 的所有帖子,这个li里面就包含需要的所有内容
title # 帖子标题
post_author # 帖子创建作者
post_time # 帖子创建时间
re_num # 总回复数
content # 最后回复内容
last_author # 最后回复者
last_time # 最后回复时间
获取下一页的url请求
请求地址: http://tieba.baidu.com/f?kw=%E6%9D%83%E5%88%A9%E7%9A%84%E6%B8%B8%E6%88%8F&ie=utf-8&pn=50
图中,可以很明确的看出,每按一次"下一页",pn将增加50,只需求拼接url地址的时候更改pn的值,就可以得到下一页的请求地址
项目实施:
新建一个get_tieba_info.py文件,代码如下:
#!/usr/bin/env python
# coding: utf-8
import urllib.request
from bs4 import BeautifulSoup
from mylog import MyLog as mylog # 导入Mylog类,起别名为mylog
class Item(object): # 定义一个Item类,爬取的字段类
title = None # 帖子标题
post_author = None # 帖子创建作者
post_time = None # 帖子创建时间
re_num = None # 总回复数
content = None # 最后回复内容
last_author = None # 最后回复者
last_time = None # 最后回复时间
class GetTieBaInfo(object):
def __init__(self, url):
self.url = url # 接收一个初始的url地址
self.log = mylog() # 实例化mylog日志类
self.pagesum = 5 # 控制爬取的页面数(一页50条数据)
self.urls = self.geturls(self.pagesum) # 获取所有下一页的urls列表
self.items = self.spider(self.urls) # 处理每一页的数据
self.pipelines(self.items) # 数据下载
'''处理url地址,return urls地址列表'''
def geturls(self, pagesum):
urls = []
pns = [str(i*50) for i in range(pagesum)] # 得到列表['0','50','100'....]即下一页的数字
# ['0', '50', '100', '150', '200']
ul = self.url.split('=') # 以=号为分隔符,切割url地址,得到下面结果
# ['http://tieba.baidu.com/f?kw', '权利的游戏&ie', 'utf-8&pn', '0']
for pn in pns:
ul[-1] = pn # ur[-1] = 0 ,50,100,150
url = '='.join(ul) # 以=为拼接符,拼接下一页的url地址
urls.append(url) # 添加到上面的urls列表中
self.log.info(u'获取URLS成功') # 写入日志文件
return urls # 返回urls地址列表
def spider(self, urls):
items = []
for url in urls:
# 调用self.getresponsecontent函数,返回整个html的内容
HtmlContent = self.getresponsecontent(url)
# 使用bs4解析器进行过滤
soup = BeautifulSoup(HtmlContent, 'lxml')
# 找到所有符合规则的li标签,返回一个list
tagsli = soup.find_all('li', attrs={'class':' j_thread_list clearfix'})
for tag in tagsli:
item = Item() # 实例化Item类
# 获取帖子标题
item.title = tag.find('a', attrs={'class':'j_th_tit '}).get_text().strip()
# 获取帖子创建作者
item.post_author = tag.find('span', attrs={'class':'frs-author-name-wrap'}).get_text().
strip()
# 获取帖子创建时间
item.post_time = tag.find('span', attrs={'class':'pull-right is_show_create_time'}).
get_text().strip()
# 总回复数
item.re_num = tag.find('span', attrs={'class':'threadlist_rep_num center_text'}).
get_text().strip()
# 最后回复内容
item.content = tag.find('div', attrs={'class':'threadlist_abs threadlist_abs_onlyline '}).
get_text().strip()
# 最后回复者
item.last_author = tag.find('span', attrs={'class': 'tb_icon_author_rely j_replyer'}).
get_text().strip()
# 最后回复时间
item.last_time = tag.find('span', attrs={'class':'threadlist_reply_date pull_right
j_reply_data'}).get_text().strip()
items.append(item) # 把所有的item,添加到items列表
self.log.info(u'获取标题为<<{}>>的项成功...'.format(item.title))
return items
def pipelines(self, items): # 接收一个items列表
filename = u'百度贴吧_权利游戏.txt'
with open(filename, 'w', encoding='utf-8') as fp:
for item in items:
fp.write('帖子标题:{}\n帖子创建作者:{}\n帖子创建时间:{}\n帖子总回复数:\
{}\n帖子最后回复内容:{}\n帖子最后回复者:{}\n帖子最后回复时间:{}\n\n\n\n'\
.format(item.title,item.post_author,item.post_time,item.re_num,item.content,item.last_author,item.last_time))
self.log.info(u'标题为<<{}>>的项输入到 {} 成功'.format(item.title, filename))
def getresponsecontent(self, url):
try:
page = urllib.request.urlopen(url)
html = page.read().decode('utf-8')
except:
self.log.error(u'python 返回 URL:{} 数据失败'.format(url))
else:
self.log.info(u'python 返回 URL:{} 数据成功'.format(url))
return html
if __name__ == '__main__':
url = 'http://tieba.baidu.com/f?kw=%E6%9D%83%E5%88%A9%E7%9A%84%E6%B8%B8%E6%88%8F&ie=utf-8&pn=0'
st = GetTieBaInfo(url)
新建一个mylog.py日志文件,代码如下:
import logging
import getpass
import sys
# 定义MyLog类
class MyLog(object):
def __init__(self):
self.user = getpass.getuser() # 获取用户
self.logger = logging.getLogger(self.user)
self.logger.setLevel(logging.DEBUG)
# 日志文件名
self.logfile = sys.argv[0][0:-3] + '.log' # 动态获取调用文件的名字
self.formatter = logging.Formatter('%(asctime)-12s %(levelname)-8s %(message)-12s\r\n')
# 日志显示到屏幕上并输出到日志文件内
self.logHand = logging.FileHandler(self.logfile, encoding='utf-8')
self.logHand.setFormatter(self.formatter)
self.logHand.setLevel(logging.DEBUG)
self.logHandSt = logging.StreamHandler()
self.logHandSt.setFormatter(self.formatter)
self.logHandSt.setLevel(logging.DEBUG)
self.logger.addHandler(self.logHand)
self.logger.addHandler(self.logHandSt)
# 日志的5个级别对应以下的5个函数
def debug(self, msg):
self.logger.debug(msg)
def info(self, msg):
self.logger.info(msg)
def warn(self, msg):
self.logger.warn(msg)
def error(self, msg):
self.logger.error(msg)
def critical(self, msg):
self.logger.critical(msg)
if __name__ == '__main__':
mylog = MyLog()
mylog.debug(u"I'm debug 中文测试")
mylog.info(u"I'm info 中文测试")
mylog.warn(u"I'm warn 中文测试")
mylog.error(u"I'm error 中文测试")
mylog.critical(u"I'm critical 中文测试")
运行get_tieba_info.py:
pycharm截图
get_tieba_info.log日志文件生成截图:
百度贴吧_权利游戏.txt截图:
代码分析:
mylog.py模块,主要是为程序提供log功能
log功能很重要,在大量爬取的时候,没有log帮助定位,很难找到错误点
主程序:
get_tieba_info.py也很简单
Item类定义需要获取的数据
GetTieBaInfo类逻辑处理
geturls方法 获取所有需要爬取的url地址
spider方法 提取每个url地址的详细内容
pipelines方法 处理数据,爬取到的数据储存方式,这里使用的是文本txt,这可以存入mysql,redis,mongo等等
getresponsecontent方法 负责发送请求,拿到影响文件(html)