赞
踩
经过一段时间的Python网络爬虫学习,今天自己摸索制作了一个能够爬取新浪微博实时热搜排名的小爬虫
1.效果:
2.制作过程中遇到的问题:
(1)一开始研究微博热搜页面的源代码时忽略了<tbody>中还包括一个置顶热搜,它的标签包含结构和其他的50个不同,因此需要单独考虑
(2)难点是标签中信息的获取,需要搞清楚 find_all( ) 函数返回的是列表,只能用于for遍历体系中,针对HTML中的<tbody>下面的<tr>节点,<tr>下面的<tb>子节点,都需要一步一步地find到
(3)在一开始不知道如何使用class="td-01"属性直接find到内容,因为python的关键字class会和class=发生重名的情况,经过查阅之后发现,可以使用 class_ 代替 class
https://blog.csdn.net/IMW_MG/article/details/78109199
(4)在获取的时候曾经遇到了问题:使用<>.string获取不到标签中的字符串内容,这种情况会在热搜排名中带有表情emoji时发生,经过调试得到了将string替换为text的解决方法,可以正常地得到字符串的内容
(5)正确地获取到一个存储信息的二维列表之后,还需要按照格式从而正确地打印出来,这又是一个难点。一直遇到了一个问题:在中英混合的字符串中,内置的len( )函数无法正确地计算字符串的字符长度(不是长度!),从而导致了输出排版不整齐的问题。经过查阅之后,有下面的内容解决了这样的问题:
https://www.jb51.net/article/86577.htm
于是内建了函数length( )来正确获取字符串的字符宽度,从而正确计算所需空格的数量
- #获取一个中英文混合的字符串文本的字符宽度部分
- widths = [
- (126, 1), (159, 0), (687, 1), (710, 0), (711, 1),
- (727, 0), (733, 1), (879, 0), (1154, 1), (1161, 0),
- (4347, 1), (4447, 2), (7467, 1), (7521, 0), (8369, 1),
- (8426, 0), (9000, 1), (9002, 2), (11021, 1), (12350, 2),
- (12351, 1), (12438, 2), (12442, 0), (19893, 2), (19967, 1),
- (55203, 2), (63743, 1), (64106, 2), (65039, 1), (65059, 0),
- (65131, 2), (65279, 1), (65376, 2), (65500, 1), (65510, 2),
- (120831, 1), (262141, 2), (1114109, 1),
- ]
- def get_width(a):
- global widths
- if a == 0xe or a == 0xf:
- return 0
- for num, wid in widths:
- if a <= num:
- return wid
- return 1
- def length(str):
- sum = 0
- for ch in str:
- sum += get_width(ord(ch))
- return sum
'运行
3.全部代码
- import requests
- import re
- import bs4
-
- #获取一个中英文混合的字符串文本的字符宽度部分
- widths = [
- (126, 1), (159, 0), (687, 1), (710, 0), (711, 1),
- (727, 0), (733, 1), (879, 0), (1154, 1), (1161, 0),
- (4347, 1), (4447, 2), (7467, 1), (7521, 0), (8369, 1),
- (8426, 0), (9000, 1), (9002, 2), (11021, 1), (12350, 2),
- (12351, 1), (12438, 2), (12442, 0), (19893, 2), (19967, 1),
- (55203, 2), (63743, 1), (64106, 2), (65039, 1), (65059, 0),
- (65131, 2), (65279, 1), (65376, 2), (65500, 1), (65510, 2),
- (120831, 1), (262141, 2), (1114109, 1),
- ]
- def get_width(a):
- global widths
- if a == 0xe or a == 0xf:
- return 0
- for num, wid in widths:
- if a <= num:
- return wid
- return 1
- def length(str):
- sum = 0
- for ch in str:
- sum += get_width(ord(ch))
- return sum
-
- #获取HTML文本
- def getHTMLText(url):
- try:
- #模拟浏览器
- kv = {'user-agent':'Mozilla/5.0'}
- r = requests.get(url, headers=kv, timeout=30)
- r.raise_for_status()
- r.encoding = r.apparent_encoding
- return r.text
- except:
- print("InternetError!")
- return " "
-
- #解析并且返回HTML文本
- def HTMLTextconvert(html):
- try:
- soup = bs4.BeautifulSoup(html, "html.parser")
- return soup
- except:
- print("HTMLConvertError!")
- return " "
-
- #检索HTML中的信息,获取搜索排名信息
- #存在置顶的情况,需要特殊判断
- def HTMLSearch(html, ranklist):
- try:
- flag = 0
- #找到所有tbody标签下的所有内容,并且遍历所有的儿子节点
- for tr in html.find("tbody").children:
- #添加判断:获得的内容是否为标签Tag类型
- if isinstance(tr, bs4.element.Tag):
- #使用flag特判置顶的情况
- if flag==0:
- rank = "置顶"
- #注意由于class属性会和python中的关键字重名,因此需要变为class_
- td02 = tr.find_all(class_=re.compile('td-02'))
- for i in td02:
- if isinstance(i, bs4.element.Tag):
- #trans得到的类型为列表
- trans = i.find_all("a")
- number = " "
- ranklist.append([rank, trans[0].string, number])
- flag = 1
- else:
- #排名信息在td标签下的class=td-01属性中
- td01 = tr.find_all(class_=re.compile("td-01"))
- for i in td01:
- if isinstance(i, bs4.element.Tag):
- rank = i.string
- #热搜内容和搜索量在td标签下的class=td-02属性中:内容是a标签,搜索量是span标签
- td02 = tr.find_all(class_=re.compile("td-02"))
- for i in td02:
- name = i.find_all("a")
- column = i.find_all("span")
- #使用string获取字符串信息不准确,因为微博还有一些热搜标题为含有表情的,因此使用了text
- ranklist.append([rank, name[0].text, column[0].text])
- except:
- print("HTMLSearchError!")
-
- #打印排名
- def Rankprint(ranklist, num):
- try:
- #先打印表头,总长为70个字符,其中{1}和{3}是变化的空格数量计算,默认为:
- #排名*4,空格*3,名称*50,空格*5,点击量*8
- a = " "
- print("——————————————————————————————————————")
- print("{0}{1}{2}{3}{4}\n".format("排名", a*5, "热搜内容", a*45, "搜索量"+a*2))
- #使用flag来判断是否输出了置顶内容
- flag = 0
- for i in range(num):
- if flag==0:
- print("{0}{1}{2}\n".format(ranklist[i][0], a*3, ranklist[i][1]))
- flag = 1
- else:
- #c是排名有一位、两位的数字,用来纠正空格
- c = 7-len(ranklist[i][0])
- #根据内容来随时计算所要填充的空格数量b
- str = ranklist[i][1]
- b = 62-length(ranklist[i][1])-len(ranklist[i][0])-c
- print("{0}{1}{2}{3}{4:<8}".format(ranklist[i][0], a*c, ranklist[i][1], a*b, ranklist[i][2]))
- print("\n")
- except:
- print("RankPrintError!")
-
- #主函数
- def main():
- try:
- #微博热搜的网站
- url = "https://s.weibo.com/top/summary?Refer=top_hot&topnav=1&wvr=6"
- #使用二维列表存储每一条热搜信息的rank信息和内容
- rank = []
- text = getHTMLText(url)
- soup = HTMLTextconvert(text)
- HTMLSearch(soup, rank)
- Rankprint(rank, 51)
- except:
- print("SystemError!")
- return 0
- main()
'运行
在所有可能发生错误的部分都增加了报错机制,这样程序就更加的具有适应能力了!
赞
踩
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。