前言
生而为人,我很抱歉 。
作为21世纪的新时代年轻人,我不看抖音,不刷快手,每日都沉浸在对知识的渴望与科研的向往中,一天不学习我就浑身难受 。
当然,我也会好奇,现在的年轻人都喜欢啥 ?
于是乎,借着学习Python的理由,写了这个小程序,这是个利用Requests
模块编写的网络爬虫。可以爬取任意百度贴吧的所有帖子。
在我年轻的那个时代里,贴吧是人手必备,那个时候,贴吧要比现在火的多,各种学校贴吧、专业交流贴吧都是热火滔天。在这其中,更加被社会各界网友封神的就是李毅吧了。
...
但是,我今天进贴吧看了看,好像李毅吧也要凉了....
时代在变啊...
程序
功能:爬取任意百度贴吧的所有帖子,获取帖子标题和链接,并保存到根目录下的Tieba.data
中。
升级空间:多线程(注意多线程文件重复打开的保存混乱问题)
存在问题:在爬取了8万多条数据后会被百度服务器识别出爬虫程序,服务器拒绝访问。
requests.exceptions.ConnectionError: HTTPSConnectionPool(host='tieba.baidu.com', port=443): Max retries exceeded with url: /f?kw=%E6%9D%8E%E6%AF%85&ie=utf-8&pn=81700 (Caused by NewConnectionError('<urllib3.connection.HTTPSConnection object at 0x000002147A0D4128>: Failed to establish a new connection: [WinError 10060] 由于连接方在一段时间后没有正确答复或连接的主机没有反应,连接尝试失败。'))
源代码:
import requests # 导入网络请求模块
from lxml import etree # 导入xpath语法分析模块
# 创建一个贴吧爬虫类
class TiebaSpider(object):
def __init__(self, tieba_name):
# 构建请求url
self.url = 'https://tieba.baidu.com/f?ie=utf-8&kw={}&fr=search'.format(
tieba_name)
# 构建请求头
self.headers = {
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36'
}
# 一个计数器
self.counter = 0
# 定义类主体运行函数
def run(self):
while True:
# 发送request请求
get_data = self.get_web_page()
# 处理返回数据
next_page_href = self.handle_data(get_data)
if next_page_href == None:
# 当返回下一页url为None时,说明贴吧爬取数据结束
break
else:
self.url = next_page_href
# 发送get请求
def get_web_page(self):
request = requests.get(self.url, headers=self.headers)
# print(request.content.decode())
return request.content
# 处理返回数据
def handle_data(self, get_data):
# 对数据解码并去除注释
result_data = get_data.decode().replace("<!--", "").replace("-->", "")
# 创建html对象
html = etree.HTML(result_data)
# 编写xpath提取语句提取所有帖子跳转a链接
a_list = html.xpath("//a[contains(@class,'j_th_tit')]")
# 循环对a标签进行信息获取
for item in a_list:
temp_dict = {}
try:
temp_dict["title"] = item.xpath('text()')[0]
temp_dict["href"] = "https://tieba.baidu.com" + \
item.xpath('@href')[0]
except:
print(f"{self.counter}有一条a链接未能获取标题或链接...")
else:
# 获取数据成功,计数器加一
self.counter += 1
print(f"## 第{self.counter}条数据写入成功!##", temp_dict)
# 写入文件
with open('Tieba.data', 'a+', encoding='utf-8') as f:
f.write(str(temp_dict))
f.write(',\n')
try:
next_page_href = 'https:' + \
html.xpath("//a[contains(text(),'下一页')]/@href")[0]
except:
next_page_href = None # 未能获取下一页链接(说明到了最后一页)
finally:
return next_page_href
if __name__ == "__main__":
my_spider = TiebaSpider('李毅')
my_spider.run()
学习笔记
- 利用Python的
Requests
模块所获得的网页源码会与在浏览器中获取的网页源码不同,因为浏览器会进行渲染,会调取JS文件; 某些网站(例如:百度贴吧)服务器返回的源码会将网页html注释起来,这样的话浏览器是可以正常识别的,但是Python的
lxml
模块在提取html元素的时候是不能识别这些的,因此对于源代码在进行lxml
提取元素前,要使用:result_data = get_data.decode().replace("<!--", "").replace("-->", "")
来去掉注释符号。
后记
昨天写了个贴吧的爬虫,本想看看现在的年轻人都喜欢看啥,但是爬了八万多条数据才发现现在玩儿贴吧的都是老年人。。。
于是乎,今天又来了斗鱼看看,听说现在的年轻人都爱这个。
源代码:
from selenium import webdriver
import time
# 创建一个爬取斗鱼网站的类
class Douyu(object):
def __init__(self):
# url
self.url = 'https://www.douyu.com/directory/all'
# 构建浏览器对象
self.brower = webdriver.Chrome()
# 类或对象运行函数
def run(self):
self.brower.get(self.url) # 发送请求
while True:
next_page_btn = self.search_data()
if next_page_btn == None:
break
else:
self.brower.execute_script(
'scrollTo(0,1000000)') # 页面较长,需要滚动才能获取元素
next_page_btn.click()
self.brower.quit()
# 获取页面数据
def search_data(self):
time.sleep(3) # 防止由于网速问题导致页面渲染未完成导致提取数据失败
# 获取所有房间li标签
room_list = self.brower.find_elements_by_xpath(
'//li[contains(@class,"layout-Cover-item")]')
for room in room_list:
temp_dict = {}
temp_dict['title'] = room.find_element_by_xpath('.//h3').text
temp_dict['type'] = room.find_element_by_xpath(
".//span[contains(@class,'DyListCover-zone')]").text
temp_dict['owner'] = room.find_element_by_xpath('.//h2').text
temp_dict['peopleNum'] = room.find_element_by_xpath(
".//span[contains(@class,'DyListCover-hot')]").text
# 写入数据
self.write_file(temp_dict)
# 寻找“下一页元素”
next_page_btn = None
try:
next_page_btn = self.brower.find_element_by_xpath(
"//span[contains(text(),'下一页')]")
except:
print("获取下一页按钮失败...")
else:
return next_page_btn
# 写入文件
@staticmethod
def write_file(content):
with open('Douyu.data', 'a+', encoding='utf-8') as f:
f.write(str(content))
f.write('\n')
if __name__ == "__main__":
douyu = Douyu()
douyu.run()
笔记:
- 不同于昨天的百度贴吧,斗鱼的前端网页与后端交互采用Ajax交互,这也就是说,每次请求新页面,地址栏URL并不会改变,所以这里在采用
Requests
模块的话显然更麻烦; - 虽然使用
selenium
中的webdriver
模块效率大大降低,但是却免去了构建POST信息的麻烦,慢就慢点吧,谁让自己太菜了。