用Python实现爬虫的包有很多,可以结合使用,但是目前个人觉得BeautifulSoup至少在看上去会更方便和美观一些。
这里只涉及静态网页的爬取,暂不支持cookie、session等。
Python实现微博热搜榜的爬取
1. requests库:比urllib2模块更简洁,request支持http连接保持和连接池,支持使用cookie保持会话,支持文件上传,支持自动响应内容的编码,支持国际化的URL和POST数据自动编码。
requests.get():用于请求目标网站,类型是一个HTTPresponse类型。
还有:requests.post()、requests.put()、requests.delete()、requests.head()、requests.options()等方法。
2. BeautifulSoup解析库:用于解析requests得到的网页,主要包括三种选择器:方法选择器(例如find、find_all等方法) 、 CSS选择器 、 节点选择器。
3. 正式进入示例:
(1). 首先导入需要的库:
importrequestsfrom bs4 import BeautifulSoup
(2). 通过url地址,使用requests包获取网页:
url = 'https://s.weibo.com/top/summary' #微博热搜
web_html = requests.get(url) #可以带更多dict格式的参数
#可以带更多dict格式的参数,形成:https://s.weibo.com/top/summary?cate=realtimehot,多个dict参数会形成:...cate=realtimehot¶m_2=value_2 格式
web_html_2 = requests.get(url,params={'cate':'realtimehot'})
bs4_2= BeautifulSoup(web_html_2.content, 'lxml')print(bs4_2.prettify())
通过得到的web_html可以获得状态码、头信息、编码格式、url地址等:
#print(web_html.status_code) # 打印状态码 --- 200#print(web_html.headers) # 打印头信息#print(web_html.content) #以字节的方式显示,中文显示为字符形式#print(web_html.text) #以text的方式显示#print(web_html.url) #url地址#print(web_html.encoding) #编码格式
(3). 通过BeautifulSoup包进行解析(bs4形式的数据都可以进行的操作):
get_text()直接获取文本形式的信息:
bs4 = BeautifulSoup(web_html.content, 'lxml') #声明bs对象和解析器,返回解析后的网页信息#print(bs4) #看起来会比较混乱一点
print(bs4.get_text()) #直接取文本,更简洁,属于字符串形式,看上去比较零散,因为有很多'\n'
prettify()粉墨登场,就变成有条理和顺序的网页文本了:
print(bs4.prettify()) #格式化代码,对齐、缩进、换行等
out_str =bs4.prettify()
with open('test_html.html','w',encoding='utf-8') as f:
f.write(out_str)
(4). 之后的操作均是在这个html文本上进行,也就是网页内容的获取 —— 涉及三种选择器:方法选择器(例如find、find_all等方法) 、 CSS选择器 、 节点选择器。
节点选择器:
#直接标签内容
print('title内容:\n', bs4.title.string) #以string格式打印出title标签中的内容
print('title标签:\n', bs4.title) #返回标题值
之间的值,因为一般只会有一个titleprint(type(bs4.title)) #注意:为bs4.element.Tag类型,同样属于bs4 ———— 也就是说你可以对这个类型的结果继续进行类似的选择操作
#也可以通过父子节点顺序读取,更为精准
print('title标签:\n', bs4.head.title) #返回标题值
之间的值,也可以嵌套获取print('title内容:\n', bs4.head.title.string)print('head标签:\n', bs4.head) #
之间的值# -----------------------------------------------------------------------------
这是原本的html内容:
#p标签的属性,均属于字典格式
print('第一个p标签:\n', bs4.p) #结果发现只输出了一个p标签,但是HTML中有3个p标签,所以该选择器的特性:当有多个标签的时候,若不特别指定,它只返回第一个标签的内容,内容是bs4.element.Tag类型的,也就是可以继续操作的格式
print('p标签的属性1:\n', bs4.p.attrs['class']) #方式一:列表,['class']是因为该p标签有这个属性,如果没有,会报错
print('p标签的属性2:\n', bs4.p['class']) #方式二:列表
print('p标签的属性3:\n', bs4.p.get('class')) #方式三:列表,不是bs4类型哦,get是字典值获取方法
print('p标签的属性4:\n', bs4.p.string) #获取
#嵌套使用
print('嵌套使用:\n', bs4.p.a) #同样未指定,且存在多个a标签(相同子标签时),取第一个a标签;结果是bs4.element.Tag类型
print('嵌套使用:\n', bs4.p.option)# #属于注释
print('嵌套使用:\n', bs4.p.contents) #获取该p标签中的所有内容,包括注释,属于列表类型
#获取子节点,每个child是bs4.element.NavigableString类型,同样属于bs4类型中的
print(bs4.p.children) #迭代器
for num, child inenumerate(bs4.p.children):print(num, child)#获取父节点
print(bs4.a.parent) #默认第一个a标签的父节点
#还有类似的:#parents 属性:输出该标签的父节点、父节点的父节点、父节点的父节点的父节点......#next_sibings 属性:输出该标签后面的兄弟标签,注意兄弟标签指的是在同一父标签下的标签#previous_sibling属性:输出该标签前面的兄弟标签,注意兄弟标签指的是在同一父标签下的标签
list_=[]for num, parent inenumerate(bs4.a.parents):print(num, parent)
list_.append(parent)print(bs4.a.next_sibings)
方法选择器:
find_all查询器:可根据标签名、属性、内容查找
find_all(self, name=None, attrs={}, recursive=True, text=None, limit=None, **kwargs)
参数:
所有attrs在使用的时候可以类似以下方式:
{'class': vaule1, 'id': value2, ...} 或者 attrs = {'class': vaule1, 'id': value2, ...} ,如果只有一个参数,也可以直接: class_ ='value1',(class_这是因为不能直接用class,会与python关键字冲突);
如果只知道名称或者值value会变化,那么也可以只通过名称,但是要将value设置为True,例如:attrs = {'class': True};
也可以将value设置为正则表达式:例如 attrs = {'class': re.compile(r'\d+')}
#获取menu
menu_div = bs4.find("div", class_='menu') #class_名称不要与class重复
menu_href_list =[]
menu_title_list=[]
menu_list=[]for kk in menu_div.find_all("a"): #取值方式:menu_div.find_all("a")[i],每一个i对应得到的都是bs4.element.Tag类型
href = kk['href']
menu_title= kk['title']
menu= kk.string #当下中的文本
menu_href_list.append(href)
menu_title_list.append(menu_title)
menu_list.append(menu)print('menu的链接是:\n',menu_href_list)print('menu的标题是:\n',menu_title_list)print('menu的内容是:\n',menu_list)
类似的方法还有:
#=============================================================================#find(name=None, attrs={}, recursive=True, text=None, **kwargs)#和find_all类似,只不过find方法是返回单个元素,如果有多个相同的结果,则返回第一个元素# #find_parents() find_parent()#find_parents()返回所有祖先节点,find_parent()返回直接父节点。# #find_next_siblings() find_next_sibling()#find_next_siblings()返回后面所有兄弟节点,find_next_sibling()返回后面第一个兄弟节点。# #find_previous_siblings() find_previous_sibling()#find_previous_siblings()返回前面所有兄弟节点,find_previous_sibling()返回前面第一个兄弟节点。# #find_all_next() find_next()#find_all_next()返回节点后所有符合条件的节点, find_next()返回第一个符合条件的节点# #find_all_previous() 和 find_previous()#find_all_previous()返回节点后所有符合条件的节点, find_previous()返回第一个符合条件的节点#=============================================================================
def find_next(self, name=None, attrs={}, text=None, **kwargs)def find_all_next(self, name=None, attrs={}, text=None, limit=None, **kwargs)def find_next_sibling(self, name=None, attrs={}, text=None, **kwargs)def find_next_siblings(self, name=None, attrs={}, text=None, limit=None, **kwargs)def find_previous(self, name=None, attrs={}, text=None, **kwargs)def find_all_previous(self, name=None, attrs={}, text=None, limit=None, **kwargs)def find_previous_sibling(self, name=None, attrs={}, text=None, **kwargs)def find_previous_siblings(self, name=None, attrs={}, text=None, limit=None, **kwargs)def find_parent(self, name=None, attrs={}, **kwargs)def find_parents(self, name=None, attrs={}, limit=None, **kwargs)def previous(self)
例如:
print(kk.find_parent()) #因为此时kk是上面的结果数据,属于bs4.element.Tag类型
# ---------------------------------------------------------------
好了,真正获取热搜的部分来了:
#获取热搜
timehot_rank_list =[]
timehot_href_list=[]
timehot_content_list=[]
timehot_num_list=[]
timehot_div= bs4.find("div", {'class':"data",'id':"pl_top_realtimehot"}) #如果是字典形式传参,则key要与html文件中的一致#timehot_tbody = timehot_div.find("tbody").get_text() #获取文本形式的数据
#timehot_tbody = timehot_div.find("tbody") #可用
timehot_tbody = timehot_div.tbody #返回第一个tbody,如果只有一个tbody,也可以直接用
return_str = ''
for ii, mm in enumerate(timehot_tbody.find_all("tr")):
rank= mm.find("td", class_ = "td-01 ranktop")
td= mm.find("td", class_="td-02")
timehot_href_list.append(td.a['href'])
timehot_content_list.append(td.a.string)if ii==0:
timehot_rank_list.append('0')
timehot_num_list.append('9999999999')
return_str= return_str +'\t'+ '0' +'\t'+ td.a.string +'\t'+ td.a['href'] +'\t'+ '9999999999' + '\n'
else:
timehot_rank_list.append(rank.string)
timehot_num_list.append(td.span.string)
return_str= return_str +'\t'+ rank.string +'\t'+ td.a.string +'\t'+ td.a['href'] +'\t'+ td.span.string + '\n'with open('微博热搜榜.txt','w',encoding='utf-8') as f:
f.write(return_str)
CSS选择器:这个更强,不过要对前端编程熟悉一点。
先总结:
class选择要加 '.'
id选择要加 '#'
tag选择不用加特殊标号
但是多重时必须要用空格隔开
css:1重选择
#1重选择
print(bs4.select('.data')) #class选择
print(bs4.select('table')) #tag选择
print(bs4.select('#pl_top_realtimehot')) #id选择
css:2重选择
#2重选择#选择class为data中的class为td-02的内容(也就是热搜标题)
aa = bs4.select('.data .td-02') #元素组成的列表
print(bs4.select('.data .td-02'))
bb= bs4.select('tr td') #微博热搜中,一个tr有3个td;所有tr的td依次排列
print(bs4.select('tr td')) #标签选择,选择所有tr标签中的td标签,实现嵌套
css:3重选择
#3重选择
print(bs4.select('tr td i')) #3重选择
css:交叉选择
#交叉选择
print(bs4.select('#pl_top_realtimehot .td-02')) #'#'表示id选择器:选择id为pl_top_realtimehot中,class为td-02的内容
print(bs4.select('#pl_top_realtimehot .td-02 a')) #3重交叉选择,选出所有热搜的地址和内容
print(bs4.select('#pl_top_realtimehot .td-02 a')[0]) #列表取值,但是每个值又是bs4类型的哦
print(type(bs4.select('#pl_top_realtimehot .td-02 a')[0])) #每一个内容的类别是:bs4.element.Tag,也属于可以继续find等选择的格式
css:另一种实现嵌套选择的方式
#另一种实现嵌套的方式
for tr in bs4.select('tr'): #对每一个查到的tr,再进行选择;因为每一个的内容格式是:bs4.element.Tag
print(tr.select('td'))print(tr.get_text()) #直接获取内容
综合以上:真正取热搜的代码如下 ~~~
importrequestsfrom bs4 importBeautifulSoup
url= 'https://s.weibo.com/top/summary' #微博热搜
web_html = requests.get(url) #可以带更多dict格式的参数
bs4= BeautifulSoup(web_html.content, 'lxml') #声明bs对象和解析器,返回解析后的网页信息
timehot_rank_list=[]
timehot_href_list=[]
timehot_content_list=[]
timehot_num_list=[]
timehot_div= bs4.find("div", {'class':"data",'id':"pl_top_realtimehot"}) #如果是字典形式,则key要与html文件中的一致#timehot_tbody = timehot_div.find("tbody").get_text() #获取文本形式的数据
#timehot_tbody = timehot_div.find("tbody") #可用
timehot_tbody = timehot_div.tbody #返回第一个tbody,如果只有一个tbody,也可以直接用
return_str = ''
for ii, mm in enumerate(timehot_tbody.find_all("tr")):
rank= mm.find("td", class_ = "td-01 ranktop")
td= mm.find("td", class_="td-02")
timehot_href_list.append(td.a['href'])
timehot_content_list.append(td.a.string)if ii==0:
timehot_rank_list.append('0')
timehot_num_list.append('9999999999')
return_str= return_str +'\t'+ '0' +'\t'+ td.a.string +'\t'+ td.a['href'] +'\t'+ '9999999999' + '\n'
else:
timehot_rank_list.append(rank.string)
timehot_num_list.append(td.span.string)
return_str= return_str +'\t'+ rank.string +'\t'+ td.a.string +'\t'+ td.a['href'] +'\t'+ td.span.string + '\n'with open('微博热搜榜.txt','w',encoding='utf-8') as f:
f.write(return_str)
View Code
参考: