python requests soup_Python爬虫之BeautifulSoup和requests

用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&param_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) #返回标题值

之间的值,因为一般只会有一个title

print(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

参考:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值