**注,小白,纯属当笔记,有错请指点,谢谢。
本文采用requests库爬取HTML页面,然后用BeautifulSoup库解析网页,再通过re库进行匹配
1.简单介绍一下requests库
#request调用参数
import requests
r = requests.get(url,params=None,**kwargs) #向服务器请求资源,返回所有内容
r.status_code #返回200即成功
r.text #响应的字符串形式
r.encoding #编码方式
#请求异常
requests.ConnectionError #连接异常 防火墙等
requests.HTTPError #http异常
requests.URLRequired
requests.ConnectTimeout #与远程服务器链接超时
requests.Timeout #整个过程超时
r.raise_for_status() #判断访问是否异常
**kwargs 参数
params
headers 修改协议头 'user-agent' 模拟各种浏览器访问 {'user-agent':'Mozilla/5.0'} 标准浏览器
json json格式数据
timeout
data
proxies 代理服务器,防止逆追踪 增加登录认证
cookies
#编程时可加入异常判断
try:
r = requests.get(url)
r.raise_for_status()
r.encoding = 'utf-8'
except:
print("异常")
2.BeautifulSoup
from bs4 import BeautifulSoup
soup = BeautifulSoup(html格式的信息,'html.parser')
#html格式信息及 requests.get返回的.text文档
3.网页结构
网页源由标签和属性组成,如下所示,具体爬取时可右键网页点击源代码查询
<p class='title'>...</p> 标签成对出现
<标签 属性>...</标签> 属性由键值对构成,
索引标签
from bs4 import BeautifulSoup
soup = BeautifulSoup(html格式的信息,'html.parser')
soup.标签
soup.标签.parent.name 父级名字
索引标签属性
soup.标签.attrs 返回字典型
soup.标签.string 字符串
索引内容
soup.find(标签,attrs={属性键值对})
soup.find_all(tag_name,attrs,recursive=True,string) 返回列表
tag_name ,attrs 可多个
recursive 是否搜索子孙节点
soup.find_all(True) 显示所有标签
索引文本信息可在后追加.text
4.网页标签的遍历
遍历方法
下行遍历
1. .contents 将<tag>的所有子节点存入列表
2. .children 遍历子节点
3. .descendants 遍历所有子孙节点 for .. in ..:
上行遍历
1. .parent 父节点
2. .parents 遍历父节点
平行遍历 (同一父节点才可以遍历)不一定返回标签
1. .next_sibling 返回下一个平行节点
2. .previous_sibling 返回上一个平行节点
3. .next_siblings 后续所有平行节点
4. .previous_sibings 前续所有平行节点
5.正则表达式基本操作
正则表达由字符串和操作符组成
. 匹配任意字符
[] 字符集,对单个字符给出范围 [abc] [a-z]
[^] 非字符集,[]的逆,排除
* 前一个字符0次或无限次扩展 abc* ab/abc/abcc/abccc...
+ 1次或无限次
? 0次或1次
| 或 abc|cdf
{m} 扩展前一个字符m次
{m,n} 扩展前一个 m到n次 产生n-m+1个值
^ ^abc abc在开头
$ abc$ abc在结尾
() 分组
\d =[0-9]
\w =[a-zA-Z0-9_]
\s 白字符 空格/换行等等
\S 非白字符
6.re库
re库功能函数
import re
re.search(pattern,string,flags=0) 搜索与正则表达式一样的地方,返回match对象,提取结果需要用 .group(0)
pattern 正则表达式,即我们要找的字符形式
string 要匹配的字符库对象,即我们的搜索范围
flag = re.I 忽略大小写
re.M ^能匹配每行
re.S .能匹配所有字符 包括换行符(默认不包括换行符)
re.match(pattern,string,flags=0) 从字符串开始位置,开始匹配 返回match对象
search和match都只返回一个匹配结果,两者区别是,match只在字符串头匹配,而search是在整个范围匹配
re.findall(pattern,string,flags=0) 搜索字符串,以列表返回所有匹配的对象 返回match对象
re.split(pattern,string,maxsplit = 0,flags=0) 按正则表达式,分割
maxsplit 最大分割数
re.finditer(pattern,string,flags=0) 返回匹配结果的迭代器
re.sub(pattern,repl,string,counts=0,flags=0) 替代所有符合的子串,返回替换后结果
repl 替换的内容
counts 最大替换数
regex = re.compile(pattern,flags=0) 将正则表达式的字符串形式编译成正则表达式对象 一次编译多次使用,
regex.search(string)
regex.match(string)
regex.findall(string)... 与上为等价形式,上述的函数都可以转化为 regex.
上面说到 search等返回的是match, match对象的主要方法有
.string 带匹配对象
.re 正则表达式
.pos 搜索的开始位置
.endpos 搜索的结束位置
.group(0) 匹配后的字符串
.start() 匹配字符串在原字符串的开始位置
.end() ... 结束的位置
.span() 开始到结束的位置
贪婪匹配
RE库采用贪婪匹配,返回匹配最长的字符串
如re.search(r'PY.*N','PYANBNCNDN')
返回 'PYANBNCNDN'
要采用最小匹配,则需要多加一个?
re.search(r'PY.*?N','PYANBNCNDN')
最小匹配方法 ?
+?
*?
??
{m,n}?
7.豆瓣评论信息爬取
def getHTMLinf(url): #向服务器发起请求
import requests
try:
r = requests.get(url)
r.raise_for_status()
r.encoding = 'utf-8'
return r.text
except:
print("出现异常")
def getLongComment(url): #获取长评论 因为在评论页并不能获取完整的长评论,因此需要提取单独用户的评论页的评论信息
html = getHTMLinf(url)
from bs4 import BeautifulSoup as bs
soup = bs(html,'html.parser')
comment = soup.find('div',attrs={'data-url':url}).text
return comment
def get_num(soup): #获取用户ID
import re
regx = re.compile('[\d]{6}')
sp = soup.find_all('div',attrs = {'class':'review-short'})[0]
result = regx.search(str(sp)).group(0)
return result
def getCOMMENTinf(url): #获取信息的总函数
html_inf = getHTMLinf(url)
from bs4 import BeautifulSoup as bs
import re
soup = bs(html_inf,'html.parser')
username_lst = []
articlename_lst = []
article_lst = []
like_lst = []
dislike_lst = []
reply_lst = []
commenttime_lst = []
ID_lst = []
lst1 = soup.find_all('header',attrs = {'class':'main-hd'})
lst2 = soup.find_all('div',attrs = {'class':'main-bd'})
count = 0
for i in lst1:
username_lst.append(i.find('a',attrs={'class':'name'}).string)
commenttime_lst.append(i.find('span',attrs={'class':"main-meta"}).text)
for i in lst2:
articlename_lst.append(i.find('h2').string)
like_lst.append(i.find('a',attrs = {'title':'有用'}).text.strip())
dislike_lst.append(i.find('a',attrs = {'title':'没用'}).text.strip())
reply_lst.append(i.find('a',attrs = {'class':'reply'}).string)
regx = re.compile('[\d]{6}')
sp = soup.find_all('div',attrs = {'class':'review-short'})[0]
result = regx.search(str(sp)).group(0)
ID = get_num(i)
ID_lst.append(ID)
com_url = i.find('a').attrs['href'] #获取评论链接
result = [username_lst,commenttime_lst,articlename_lst,article_lst,like_lst,dislike_lst,reply_lst,ID_lst]
lstname =['username_lst','ID_lst','commenttime_lst','articlename_lst','article_lst','like_lst','dislike_lst','reply_lst']
article_lst.append(getLongComment(com_url))
return result,lstname
def change_datatype(data,lstname,i): #将得到的数据转为DataFrame格式
from pandas import DataFrame
import numpy as np
n = len(data[0])
result = DataFrame(data,index = lstname,columns = np.arange(i*n,i*n+n) )
return result
import numpy as np
if __name__=='__main__':
start_url = 'https://movie.douban.com/subject/1291841/reviews?start='
depth = 2 #爬取的页数
for i in np.arange(depth):
url = start_url+str(i*20)
# try:
data,lstname = getCOMMENTinf(url)
if i == 0:
result = change_datatype(data,lstname,i)
else:
result_tem = change_datatype(data,lstname,i)
result = result.join(result_tem,how = 'inner')
# except:
# print('error')
result = result.T
result.to_csv("C:/Users/Administrator/Desktop/result_commetn.csv")
8.结果