一、爬取网页源代码
1、下载网页的html源代码
from bs4 import BeautifulSoup
def handlechapter(url):
response = urllib.request.urlopen(url)
html = response.read().decode('utf-8')
return html
html = handlechapter(url) #url为某贴吧首页的链接
#这里为url = 'http://tieba.baidu.com/f?kw=%E4%B8%9C%E5%8C%97%E5%A4%A7%E5%AD%A6%E7%A7%A6%E7%9A%87%E5%B2%9B%E5%88%86%E6%A0%A1&ie=utf-8&pn=0'
注意网页的源代码为utf-8编码
2、将html文件转换为python文件
url_soup = BeautifulSoup(html, 'html.parser')
Beautiful Soup将复杂HTML文档转换成一个复杂的树形结构,每个节点都是Python对象,所有对象可以归纳为4种: Tag , NavigableString , BeautifulSoup , Comment
【1.Tag类对象】
即html中的标签,BeautifulSoup对象(以上面代码中的url_soup为例)可以直接在后面接上某种标签的名称,得到的对象是该种标签在这段html中的第一个实例。
比如对于print(url_soup.h)
>>>
(bs里的html对象是可打印的,打印出来就是html原文)
每个Tag类对象都有两个属性,name和attrs。
name就是标签的名字,而attrs是个字典,里面记录了所有这个tag的属性值。比如有tag是
其name就是u'h1',而attrs是{u'class':[u'space-seo',u'space-meta'],u'name':u'testname'} //注意区别tag对象的name属性和写在attrs里面的name属性的区别,另外所有被存到变量里的html内容全部都变成了unicode对象。在print的时候可以指定encode,默认encode是utf-8。还有,所有class属性都默认都被解析成一个列表,即便只有一个class值,也会成为[u'class_value']
当然,在定位到Tag对象之后可以获取查看其属性值,另一方面,也可以对其属性值等进行修改,修改完了之后就是存在内存中的这个变量里面的,最终可以输出成文件的形式。
【2.String对象】 //selenium用的是text来指代标签内部的文本内容,而bs用的是string。而且对于
String对象用于代表每个元素所含的文字部分,在标签后面加上.string即可调用
注意,某个Tag对象的子节点以及子节点里面的内容都不算进string,当一个元素(比如
【3.BeautifulSoup对象】
指代整个文档的对象。可以视为是的对象。其实之前创建的BeautifulSoup对象soup,就是指代整个html文档
【4.comment对象】
用于代表某个元素内的注释
二、清洗出想要的数据
先上一段单个帖子的格式:
6
学校周边有什么适合学习的地方吗? 工学馆被封。图书馆人太多
不难发现,我们想要的内容在两个地方:一个是两个标签里的内容(字符串),另一个是标签某个属性的值。
1、先从简单的看,直接获取标签内部的文本内容:
我们想要的标题,回复数,最后回复时间都属于这种类型,筛选也很简单,如想获取第一个a标签下的内容
print(url_soup.a.get_text())
或
print(url_soup.a.string)
但网页源代码中有很多a标签,要找到我们想要的那个需要给它附加一些条件:
以找置顶帖的个数为例:
zd_num = 0
for zd in url_soup.find_all("li", class_="thread_top"):
zd_num = zd_num + 1
在bs库中,".find_all"是找所有的此类标签,格式为"find_all("标签",标签属性="***")
当然find的话就是找第一个,需要注意的是,class属性要写成class_
同样的,标题和回复数也可以这样得到
for c in url_soup.find_all("div",class_="j_th_tit"):
title = c. find_all("a")[0].get_text().strip()
# print("[",title,"]")
Title.append(title)
for b in url_soup.find_all("div",class_="j_threadlist_li_left"):
num = b.find_all("span")[0].get_text().strip()
# print("回复数:",num)
Num.append(num)
这两个都是在div标签下唯一的一个内容,只需要筛选出div,再筛选对应的标签就可以(当然也可以用正则表达式来筛选)
但是最后回复时间有些不一样,因为在该div下有两个span标签,如:
起初的想法和上面一样,筛选出这个div-->筛选出想要的span标签-->打印出span下面的字符串
for a in url_soup.find_all("div", class_="pull_right"):
aa = a.find_all("span")[1].get_text().strip()
#time = aa.find(title=re.compile("最后回复时间"))
# aa = a.find("span",class_="threadlist_reply_date pull_right j_reply_data")
# if (str(aa)=="None"):
# time = " "
# else:
# time = aa.get_text().strip()
# #print(time)
Time.append(aa)
前两步都实现了,但是在打印最后字符串的时候却一直出现问题(现在我在怀疑是不是格式错误的问题)。后面突然想到这个div里的span内容都是成对出现,为何不在打印的时候选择一下数组次序呢,索性就不找这个问题了~
2、选取标签里属性的值
同样要先从div开始筛选,先选择进入a标签,在获取属性值的时候可以用.get()函数,如下:
for f in url_soup.find_all("div",class_="j_th_tit"):
ff = f.a
fff = ff.get('href')
#print(fff)
ffff = "http://tieba.baidu.com"+fff
Href.append(ffff)
获得了想要的链接。
参考了一些很好的博客,附上官方中文文档和一些觉得总结的好的文章:
BeautifulSoup中文官方文档
https://www.cnblogs.com/zipon/p/6129280.html
https://www.cnblogs.com/zhaof/p/6930955.html
https://www.cnblogs.com/yizhenfeng168/p/6979339.html
https://www.cnblogs.com/my1e3/p/6657926.html
最后附上完整代码
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36'}
from bs4 import BeautifulSoup
import urllib.request
import re
Title = []
Num = []
Href = []
Time = []
def handlechapter(url):
response = urllib.request.urlopen(url)
html = response.read().decode('utf-8')
return html
url = 'http://tieba.baidu.com/f?kw=%E4%B8%9C%E5%8C%97%E5%A4%A7%E5%AD%A6%E7%A7%A6%E7%9A%87%E5%B2%9B%E5%88%86%E6%A0%A1&ie=utf-8&pn=0'
url1 = 'http://tieba.baidu.com/f?kw=%E4%B8%9C%E5%8C%97%E5%A4%A7%E5%AD%A6%E7%A7%A6%E7%9A%87%E5%B2%9B%E5%88%86%E6%A0%A1&ie=utf-8&pn=50'
html = handlechapter(url)
url_soup = BeautifulSoup(html, 'html.parser')
zd_num = 0
for zd in url_soup.find_all("li", class_="thread_top"):
zd_num = zd_num + 1
#print(zd_num)
for a in url_soup.find_all("div", class_="pull_right"):
aa = a.find_all("span")[1].get_text().strip()
#time = aa.find(title=re.compile("最后回复时间"))
# aa = a.find("span",class_="threadlist_reply_date pull_right j_reply_data")
# if (str(aa)=="None"):
# time = " "
# else:
# time = aa.get_text().strip()
# #print(time)
Time.append(aa)
for b in url_soup.find_all("div",class_="j_threadlist_li_left"):
num = b.find_all("span")[0].get_text().strip()
# print("回复数:",num)
Num.append(num)
for c in url_soup.find_all("div",class_="j_th_tit"):
title = c. find_all("a")[0].get_text().strip()
# print("[",title,"]")
Title.append(title)
for f in url_soup.find_all("div",class_="j_th_tit"):
ff = f.a
fff = ff.get('href')
#print(fff)
ffff = "http://tieba.baidu.com"+fff
Href.append(ffff)
# for x in range(0,50):
# print("[",Title[x],"]")
# print("回复数:",Num[x])
# print(" 链接:",Href[x])
# if (x