用python爬取豆瓣《教父》影评等信息

**注,小白,纯属当笔记,有错请指点,谢谢。

本文采用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.结果


猜你喜欢

转载自blog.csdn.net/donger__chen/article/details/80708728