逝者如斯夫,不舍昼夜!
原理:模拟浏览器访问网页的方式向HTTP发起请求,获取响应Response数据
案例:爬取豆瓣前250电影信息
相关库
re 正则表达式
bs4 对html,json等格式数据进行解析
urllib http请求库,获取数据
xlwt Excel相关操作
sqlite3 sqlite数据库操作
主流程
数据爬取
- 生成请求对象
Request(url, data=None, headers={ }, origin_req_host=None,unverifiable=False, method=None) |
---|
参数 | 说明 |
---|---|
url | 请求地址 |
data | 提交给网址的表单数据 |
headers | 通过修改User-Agent来伪装浏览器 |
origin_req_host | 请求方的host或者IP |
unverifiable | 表示这个请求是无法验证的,默认是False。例如,我们请求一个HTML文档中的图片,但是我们没有自动抓取图像的权限,这时unverifiable的值就是True |
method | 请求使用的方法,比如GET、POST |
封装请求对象,包括url,头部信息等
def getHeaders(url):
headers={
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36"
};
dict={"hello":"word"}
data=bytes(urllib.parse.urlencode(dict),encoding="utf-8")
req=urllib.request.Request(url=url,data=data,headers=headers,method="GET")
return req
- 获取数据
通过urllib库中的urlopen打开一个url对象,在使用read()方法对源码解析
def GetData(url):
req= getHeaders(url)#封装的request对象
try:#异常捕获
#urlopen无法传参数,所以需要声明一个request对象,通过对象添加参数
reponse=urllib.request.urlopen(req,timeout=10);#打开一个携带头部信息的url对象,也可以直接是一个url路径
print(reponse.read().decode("utf-8"))#read对源码解析,decode:使用utf-8格式进行解析
#print(reponse.status)#状态码
#print(reponse.getheaders())#列表的形式展现出响应头
#print(reponse.getheader("Date"))#获取指定的响应头
except urllib.error.URLError as e:
if isinstance(e.reason,socket.timeout):
print(" timeout=1,now time out!")
数据解析
在这里使用BeautifulSoup4库,它默认支持Python的标准HTML解析库;它将复杂HTML文档转换成一个复杂的树形结构,通过下面的四大对象检索HTML树
BeautifulSoup4四大对象 | 对象类型 | 重要属性 | 备注 |
---|---|---|---|
Tag | bs4.element.Tag | name 、 attrs | |
NavigableString | bs4.element.NavigableString | string | |
BeautifulSoup | bs4.BeautifulSoup | name、attrs | 大部分时间会当做一个Tag使用 |
Comment | bs4.element.Comment | string | 特殊的NavigableString ,当标签内容含有注释时,会将注释符号替换掉 |
1.Tag、BeautifulSoup用法
bs.a#访问首个a标签的所有内容
bs.a.name#输出首个a标签的名称,即a
bs.a.attrs#输出首个a标签的属性,得到一个字典
2.NavigableString、Comment用法
bs.a.string#访问首个a标签内的文本内容
找到符合要求的所有的节点或节点内容
方法:find_all(name, attrs, recursive, text, **kwargs) |
---|
方法参数说明及使用
参数 | 说明 | 使用方法 |
---|---|---|
name | 查找与字符串完全匹配的内容 | bs.find_all(“a”) ;正则方式:bs.find_all(re.compile(“a”)); 列表方式:bs.find_all([“meta”,“link”]); 方法匹配:bs.find_all(GetName) |
attrs | 搜索特殊属性的tag | bs.find_all(attrs={“data-foo”:“value”});若使用bs.find_all(data-foo=“value”)会报错 |
text | 搜索文档中的字符串内容,接受 字符串,正则表达式,列表 | bs.find_all(text=“hao123”); 正则:bs.find_all(text=re.compile("\d")) ;列表:bs.find_all(text=[“hao123”, “地图”, “贴吧”]) ; 方法:略 |
kwargs | bs.find_all(id=“head”) ;find_all(href=re.compile(“http://news.baidu.com”)); class加_区分py中的关键字 bs.find_all(class_=True) |
此外还有find()方法,将返回符合条件的第一个Tag,有时我们只需要或一个Tag时,我们就可以用到find()方法了。当然了,也可以使用find_all()方法,传入一个limit=1,然后再取出第一个值也是可以的,不过未免繁琐
bs.find("title")#返回只有一个结果的列表
bs.find("div").find("div")
#等价于
bs.find("div").find("div")
3.CSS选择器
使用CSS选择器的语法找到Tag:
查找方式 | 使用方法 |
---|---|
标签名 | bs.select(‘a’) |
类名 | bs.select(’.mnav’) |
ID | bs.select(’#u1’) |
组合 | bs.select(‘div .bri’) |
属性 | bs.select(‘a[class=“bri”]’) |
子标签 | bs.select(‘a[class=“bri”]’) |
兄弟标签 | bs.select(".mnav ~ .bri") |
获取内容 | bs.select(‘title’)[0].get_text() |
4.HTML树的遍历
bs.a.contents#将首个a标签下的所有子节点(不包括孙子节点,兄弟节点)以列表的形式呈现出来
bs.a.children#将首个a标签下的所有子节点(不包括孙子节点,兄弟节点)以迭代器的形式呈现出来
5.正则表达式(re库)
方法 | 使用 | 返回 | 说明 |
---|---|---|---|
re.search(pattern,string)} | re.search( r"\d" , CB2A ) | <_sre.SRE_Match object; span=(2, 3), match=‘2’> | 从string的开始位置搜索,没有就继续搜索, |
re.match(pattern,string) | re.match(r’\d’,‘1CBA’) | <_sre.SRE_Match object; span=(0, 1), match=‘1’> | 在字符串开始位置匹配,开始位置没有返回NONE |
re.findall(pattern,string) | re.findall(r’\d’,‘C3B2A1’) | [‘3’, ‘2’, ‘1’] | 列表形式返回匹配项 |
re.split(pattern,string) | m=re.split(r’\d’,‘C3BA’) | [‘C’, ‘BA’] | 分割字符串,返回列表 |
re.sub(pattern,replace,string) | re.sub(r’\d’,‘NN’,‘C3B2A1’) | CNNBNNANN | 用repl替换 pat匹配项 |
声明全局变量
pictureLink=re.compile(r'<a\s+class=""\s+href="(.*?)">')#详情链接
ChinAndEngName=re.compile(r'<span\s+class="title"\s*>(.*?)</span>')#中英文名
Author=re.compile(r'<p\s+class=""\s*>(.*?)</p>',re.S)#详细信息
Score=re.compile(r'<span\s+class="rating_num"\s+property="v:average">(\d*.\d*)</span>')#分数
pj=re.compile(r'<span>(.*?)</span>')#简介
解析
def parseData(url):
reponse=GetData(url)#获取html
bs=BeautifulSoup(reponse,"html.parser")#生成html树
datalist=[]
for item in bs.find_all("div",class_="info"):#获取class="info"的div及子元素
data=[]
data.append(re.findall(pictureLink,str(item))[0])#详情链接
data.append(re.findall(ChinAndEngName,str(item))[0])#中文名
if len(re.findall(ChinAndEngName,str(item)))==1: #英文名
data.append("")
else:
data.append(re.sub(r'\s*/\s*','',re.findall(ChinAndEngName,str(item))[1]))
data.append(re.sub(r'[\s,<br/>]','',str(re.findall(Author,str(item))[0])))#电影详情
data.append(re.findall(Score,str(item))[0])#分数
data.append(re.findall(pj,str(item))[0])#评价
datalist.append(data)
return datalist
数据保存
def saveData(datalist):
book=xlwt.Workbook(encoding="utf-8")#创建wookbook对象
sheet=book.add_sheet('豆瓣电影',cell_overwrite_ok=True)//创建可覆盖得到工作表
col=("详情链接","中文名称","英文名称","详情","评分","评价");
for i in range(0,6):
sheet.write(0,i,col[i])#向第行i列写入数据col[i]
for i in range (0,250):
data=datalist[i]
for j in range(0,6):
sheet.write(i+1,j,data[j])
book.save("豆瓣电影.xls")
main调用
url="https://movie.douban.com/top250?start="
datalist=[];
for i in range(0,10):
data=parseData(url+str(25*i))
for j in range(0,25):
datalist.append(data[j])
#print(len(datalist))
saveData(datalist)