实战内容:访问微博热搜榜(Sina Visitor System),获取微博热搜榜前50条热搜名称、链接及其实时热度,并将获取到的数据通过邮件的形式,每20秒发送到个人邮箱中。
话不多说,先放注意事项:
- 定义请求头
本实验需要获取User-Agent、Accept、Accept-Language、Accept-Ecoding、Cookie五个字段,前四个字段可能都是相同的,主要是Cookie不同。具体获取流程如下:
- 打开目标网页,本实验目标网页为Sina Visitor System
- 按键盘上面F12进入开发者模式,此时页面如下:
- 按键盘上面F5刷新页面,此时开发者模式中会有网页相关信息,页面如下:
- 依次点击Network、All、以及summary(即目标链接的地址),各个位置如下图所示:
- 点击summary后出现右侧窗口,点击Header能够得到相关报文字段,如下图所示:
- 本实验所需的字段均在Request Header中,即下图中标注的字段。具体字段对应的值以各自浏览器为主,无需与图中字段值相同。
- 记住User-Agent、Accept、Accept-Language、Accept-Ecoding、Cookie五个字段值,后续在代码中会用到。(建议使用文本保存,以便使用)
除此之外,我们还需要获取一个非常重要的东西,来帮助我们实现邮箱发送功能。
2.获取发送邮箱的smtp授权码
smtp授权码只需要发送邮箱的,不是接收邮箱的。因为不是我们自己主动发送邮件,通过第三方(python代码)发送的,所以需要获取smtp授权码进行第三方登录。本实验以QQ邮箱为例,获取QQ邮箱的smtp授权码,其他类型邮箱类似或者可以上网查找相关资料。
- 打开QQ邮箱设置-账户,往下滑找到如图所示的选项。
- 将上图中的四个服务全开起来,具体操作步骤略,点击开启的时候会有相关提示。
- 点击图中生成授权码,并根据提示进行操作。
- 操作过后会得到下面的提示框,记住显示的授权码,备用。
本次实战的重点注意部分:
其他说明
浏览微博热搜榜html网页源码可发现整个热搜榜内容均在
…,每条热搜的信息(序号、关键词、热度)均在… 标签内。本实验还是使用CSS选择器获取指定元素信息。具体某条热搜链接和关键词的CSS选择器为:
#pl_top_realtimehot > table > tbody > tr:nth-child(1) > td.td-02 > a
为获取全部热搜的CSS选择器,将上面的改为:
#pl_top_realtimehot > table > tbody > tr> td.td-02 > a
同理,获取全部热搜热度的CSS选择器为:
#pl_top_realtimehot > table > tbody > tr > td.td-02 > span
热搜榜置顶没有显示热度,需要特殊处理。另外就是部分热搜的链接会出现别的情况,也需要特殊处理。
老规矩,先上我们实战需要的库和包
除过之前我们需要的老三样,这次还需要smtplib、MIMEText、MIMEMultipart三个库和相关包。
前三个库和包的作用,之前的文章(经典网络爬虫实践内容---豆瓣网页内容爬取(初级)-CSDN博客)已经讲过,所以这里就不再赘述了。引入smtplib、MIMEText、MIMEMultipart三个库和相关包,是分别为了能够实现,邮件发送(stmplib)、邮件内容操作(MIMExxxxx)等两个功能。
请求网页函数,同样也已经讲过(为了降低文章冗余性,本次实战内容中,之前已经涉及的模块将不再讲解)建议先阅读(经典网络爬虫实践内容---豆瓣网页内容爬取(初级)-CSDN博客)以及(经典网络爬虫实践内容---豆瓣网页内容爬取(完结)-CSDN博客)两篇内容,再进行本次实战,将会更容易一些。
解析网页函数:
和之前相同的是,我们解析网页,通过CSS选择器进行选择标签。
因为本次实验需要将或许到的热搜榜单发送到邮箱内,且考虑到stmp协议,并不支持发送二进制内容,所以在这里我们需要将获取到的内容,全部以字符串的形式存储,因此我们声明了集合与列表,用来暂存我们所获取到的内容,在最后一步统一转换为规定好的字符串格式,这里与之前不同的是,对微博热搜名称的获取时,我们使用了strip()函数来去除获取内容中的空格,避免因为空格存在造成的一些小bug。除过之前使用的get_text()和get()函数,这里的两种用法也可以实现相同的目的。这一段内容是获取头热搜榜单,也就是前面我们提到的,不存在热度的热搜内容。所以我们人工规定它的热搜度为“无”。接着将获取到的内容存入列表中,且追加入集合中。
紧接着上面的内容,我们开始对其余内容进行获取。(可能你会疑问,微博热搜不是50条吗,为什么我的范围是从1-52,这里的解释是:第0条就是我们刚才获取到的没有热度的热搜内容,且最后一位(52)是不可取的,仔细观察发现在热搜内容中还有一条是夹杂在热搜中间的,没有序号,也没有热度,并且链接所在的标签也是与众不同,前面提到,可能会有特殊的热搜需要特殊处理,就是此处)在这里,对热度的一个获取,需要进行进一步处理。因为获取到的内容中还包含热搜类别,我们只需要获取数值就可以了。对于这条特殊热搜,我们的处理方式是,直接获取href类型的标签,这条特殊链接的href标签内容固定为“javascript:void(0);”,并且它的真实链接位于href_to中,所以我们只需要判断,当一个热搜内容的href标签内容为字符串“javascript:void(0);”时,改为获取它的href_to标签。我使用print的目的是为了查看内容是否成功获取,并进行对代码修正,算是一种另类的debug方式。
前面说到,stmp协议不支持传输二进制,所以我们需要将获取到的内容转换为字符串,这里将列表中的全部内容,按照固定的格式,进行转换并且获取系统的当前时间,作为一个时间戳,来验证是否是每20秒发送一次。将转换好的字符串交给EmailQQ()函数进行转发,解析网页函数的功能就结束了。
邮件发送函数EmailQQ():
在这里发送人、接收人邮箱都是自己的邮箱账号,stmp写入自己的授权码(前面获取的)使用MIMEMultipart()创建一个对象,将字符串以utf-8的格式重新编码,存入对象中(这个发送过去就是一长串的汉字和链接,当然还有另一种方式,以附件的形式传输)
在项目文件夹下创建一个txt文本,将news的内容装入txt中,并将txt变为附件,加入到mer中
最后赋予mer一个主题,并将发送人和接收人信息传给mer,使用smtplib对邮件进行传输,首先登陆用户,发送邮件后,及时退出,并返回发送成功信息。EamilQQ()函数的功能就结束了。
最后是main函数:
按照对应的内容,编写好请求头内容,准备好url,写一个while循环,来实现每20秒执行一次
(记得测试完成后结束程序!!!不然第二天你会收到上千封邮件!!!!!!)
完整代码如下:
import requests from bs4 import BeautifulSoup import time import smtplib from email.mime.text import MIMEText from email.mime.multipart import MIMEMultipart #请求网页 def page_request(url,us): response=requests.get(url,headers=us) html=response.content.decode('utf-8') return html #将爬取的数据进行打包,准备发送至个人邮箱 def EmailQQ(news): #发送人邮箱 number = '[email protected]' #授权码 smtp = 'xxxxxxxxxxxxxxx' #接收人邮箱 to = '[email protected]' # 可以是非QQ的邮箱 #==================================================仅文字 #创建一个对象 mer = MIMEMultipart() #将获取的字符串以utf-8的格式进行编码便于后续操作 mer.attach(MIMEText(news, 'html', 'utf-8')) print(news) #==================================================实现附件格式 #打开微博热搜.txt并将获取到的字符串写入 with open('微博热搜.txt', 'w', encoding='utf-8') as f: f.write(news) #将微博热搜里的内容读取到创建的对象里 fujian = MIMEText(open('微博热搜.txt', 'rb').read(), 'base64', 'utf-8') #附件内容及文件名称 fujian["Content-Type"] = 'application/octet-stream' fujian.add_header('Content-Disposition', 'file', filename=('utf-8', '', '微博热搜.txt')) #附加刚才创建的txt内容 mer.attach(fujian) #=================================================== mer['Subject'] = '每日微博热搜榜单' # 邮件主题 mer['From'] = number # 发送人 mer['To'] = to # 接收人 #发送邮件 s = smtplib.SMTP_SSL('smtp.qq.com', 465) #登录用户 s.login(number, smtp) #发送邮件 s.send_message(mer) #退出登录 s.quit() print('成功发送') #解析网页 def page_parse(html): soup=BeautifulSoup(html,'html.parser') #创建一个列表将之后获取到的信息存入该列表中 news=[] #创建集合进行临时存放 new={} hot_topics = soup.select(".td-02 > a") # 获取前50条热搜的名称和链接 top_name=hot_topics[0].text.strip() top_link=hot_topics[0]["href"] new['name']=top_name new['link']=top_link new['top']='无' news.append(new) i=0 for topic in hot_topics[1:52]: name = topic.text.strip() # 热搜名称 link = topic["href"] # 热搜链接 # 实时热度可能以其他方式呈现 hot_score = soup.select(".td-02 > span")[i].get_text() hot_scores=hot_score.split(" ")[1] i+=1 #针对时刻变化的链接进行特殊处理,获取“href_to”中的链接 if link=="javascript:void(0);": new = {} link = topic["href_to"] new['name'] = name new['link'] = link new['top'] = '无' news.append(new) #对于普通链接进行普遍处理 else: new = {} new['name'] = name new['link'] = link new['top'] = hot_scores news.append(new) print(news) #创建一个字符串用于将列表中的内容放入字符串中,以方便邮件的发送 content = '' #将获取到的所有内容以固定格式转化为字符串形式 for i in range(len(news)): content += news[i]['name'] + '\n' + '链接:https://s.weibo.com' + news[i]['link'] + '\n' + '热度:' + news[i]['top'] + ' \n' #获取当前时间 get_time = time.strftime('%Y-%m-%d %X', time.localtime(time.time())) + '\n' #将获取到的时间追加值字符串中 content += '获取事件时间为' + get_time #执行邮件发送任务 EmailQQ(content) if __name__=='__main__': print("开始爬取") ua={ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36 Edg/119.0.0.0", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7", "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6", "Accept-Ecoding":"gzip, deflate, br", "Cookie":"SCF=AnpXG01ZxY1wAyfDudkAT2_hIwp73gb1dIW6vESyvce-9GALd9FEXCOE7Az7BlC5pMnwUs-hc7cUFW6riwJykIA.; SUB=_2AkMSDpm8f8NxqwFRmfoUyG3nao9yzwjEieKkUmhnJRMxHRl-yT9kqhAPtRB6OY63UzpsBLDqTFgSSjLRQhoTnyhZbLTT; SUBP=0033WrSXqPxfM72-Ws9jqgMF55529P9D9WWUwZjIKHGEoe.arpQlbSZM; _s_tentry=-; Apache=6416867210035.957.1699878579813; SINAGLOBAL=6416867210035.957.1699878579813; ULV=1699878579827:1:1:1:6416867210035.957.1699878579813:" } url = f'https://s.weibo.com/top/summary' #设置循环每隔20s获取一次热搜信息并进行发送 while True: #发出请求 html = page_request(url, ua) #解析网页 page_parse(html) #睡眠 time.sleep(20)
老样子,如果觉得内容还可以,麻烦动动小手点个赞,谢谢啦!