Python网络爬虫与信息提取(一)

看了原视频网站的教学视频,感觉内容讲解深入浅出,为加深个人理解,总结如下。
0.学习思路
Requests库:自动爬取HTML页面,自动网络请求提交。
Robots.text: 网络爬虫排除标准。
Beautiful Soup: 解析HTML页面。
project1: 2019年中国最好大学排名爬取
1.Requests库
安装方式同一般Python库的安装:Windows系统打开cmd,输入pip install requests
1.1 Requests库的七个基本函数

方法说明
requests.request()构造一个请求,支撑以下各种方法的基础方法
requests.get()获取HTML网页的主要方法,对应HTTP的GET
requests.head()获取HTML网页头信息的方法,对应于HTTP的HEAD
requests.post()向HTML网页提交POST请求的方法,对应于HTTP的POST
requests.put()向HTML网页提交PUT请求的方法,对应于HTTP的PUT
requests.patch()向HTML网页提交局部修改请求,对应于HTTP的PATCH
requests.delete()向HTML网页提交删除请求,对应于HTTP的DELETE

上述七个方法中,都以requests.request()函数为基础,故用该函数也能将后六种函数表述出来。网络爬虫主要使用requests.get()函数。
1.2 requests库的一些常用函数:
.status_code 查看状态

import requests
r = requests.get("url")
r.status_code    #输出运行状态码,若为200,则说明get成功,否则get失败

.encoding 通过访问Heading信息确定编码方式。

r.encoding    输出:'utf-8'

.apparent_encoding 从内容中分析出的响应内容编码方式(备选编码方式)。
apparent_encoding为备选编码方案,当encoding无法返回编码方式,可用该方法进行查询。故该方法更为准确。当r.status_code返回错误,或encoding编码生成结果出现乱码时,采用r.encoding=r.apparent_encoding再输出即可。

r.apparent_encoding  输出:‘utf-8’

.headers 输出头部信息

r.headers    
输出:{'Server': 'bfe/1.0.8.18', 'Date': 'Thu, 28 Feb 2019 06:25:24 GMT', 'Content-Type': 'text/html', 'Last-Modified': 'Mon, 23 Jan 2017 13:28:24 GMT', 'Transfer-Encoding': 'chunked', 'Connection': 'Keep-Alive', 'Cache-Control': 'private, no-cache, no-store, proxy-revalidate, no-transform', 'Pragma': 'no-cache', 'Set-Cookie': 'BDORZ=27315; max-age=86400; domain=.baidu.com; path=/', 'Content-Encoding': 'gzip'}

.content HTTP响应内容的二进制形式(二进制图片)
1.3 模拟浏览器发布请求方法
requests库使用的基本程序

import requests
r = requests.get(url)
r.status_code    #查询返回状态
r.encoding = r.apparent_encoding    
r.text

但是,有些网站具备识别功能,识别出为计算机访问时便阻止访问。我们可以查看一下自己的请求。

r.request.headers
输出:{'User-Agent': 'python-requests/2.18.4', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive'}

可以发现,这里的User-agent为python-requests/2.18.4,即识别为python访问而非浏览器,故为使得访问通过,需要进行代码的改进。
模拟浏览器访问

kv = {'user-agent':'Chrome'}
r=requests.get(url, headers = kv)
r.status_code
r.encoding = r.apparent_encoding
r.requests.headers
输出:{'user-agent': 'Chrome', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive'}

即可通过。
爬取网页的通用代码框架

import requests
def getHTMLText(url):
    try:
        r = requests.get(url, timeout = 30)
        r.raise_for_status()    #如果状态不是200,引发error异常
    except:
        return "产生异常"

if _name_ == "_main_":
    url = ""
    print(getHTMLText(url))

2. HTTP协议与Robots协议
HTTP,Hypertext Transfer Protocol,超文本传输协议HTTP是一个基于“请求与响应”模式的、无状态的应用层协议(用户,服务器) URL格式:http://host[:port][path] host:合法的Internet主机域名或IP地址 port:端口号,缺省端口为80 path:请求资源的路径。
Robots Exclusion Standard 网络爬虫排除标准作用:网站告知网络爬虫哪些页面可以抓取,哪些不行。 形式:在网站根目录下的robots.txt文件
3. BeautifulSoup(美丽汤)
美丽汤解析模板:

from bs4 import BeautifulSoup
soup = BeautifulSoup('<p>data</p>','html.parser')

BeautifulSoup的基本元素

  1. BeautifulSoup库是解析、遍历、维护“标签树”的功能库。
  2. p标签:
<p class="title">...</p>   #<p>..</p>: 标签Tag   Name/属性Attributes(0个或多个)

BeautifulSoup的基本元素:

名称解释说明
Tag标签,最基本的信息组织单元,分别用<>和</>标明开头和结尾。
Name标签的名字,格式:<标签> .name
attributes标签的属性,字典形式组织,格式:<标签>.attrs
NavigableString标签内非属性字符串,<>…</>中字符串,格式<标签>.string
Comment标签内字符串的注释部分,一种特殊的Comment类型

3.1 基本元素程序示例
3.1.1 标签(Tag)

from bs4 import BeautifulSoup
soup = BeautifulSoup(demo,"html.parser")
soup.title
输出:<title>
        权力的游戏 第一季 (豆瓣)
</title>

获取标签内容

tag = soup.a     #获取标签内容(只能获取一个)
tag
输出:<a class="nav-login" href="https://accounts.douban.com/passport/login?source=movie" rel="nofollow">登录/注册</a>

通过获取标签名,进行上下的遍历:

from bs4 import BeautifulSoup
soup = BeautifulSoup(demo,"html.parser")
soup.a.name
输出:‘a’
soup.a.parent.name
输出:'div'
soup.a.parent.parent.name
输出:‘div’

3.1.2 属性
通过已知标签名,获得该标签下的所有属性

tag=soup.a
tag.attrs
输出:{'class': ['nav-login'],
 'href': 'https://accounts.douban.com/passport/login?source=movie',
 'rel': ['nofollow']}

通过已知标签名的属性名,获得属性内容

tag.attrs['href']
输出:'https://accounts.douban.com/passport/login?source=movie'
tag.attrs['class']
输出:['nav-login']

3.1.3 字符串
通过美丽汤的标签,获得该标签下的字符串内容:

soup.a
输出:<a class="nav-login" href="https://accounts.douban.com/passport/login?source=movie" rel="nofollow">登录/注册</a>
soup.a.string
输出:'登录/注册'

3.1.4 Comment

from bs4 import BeautifulSoup
newsoup=BeautifulSoup("<b><!--This is a comment--></b><p>This is not a comment</p>","html.parser")
newsoup.b.string
输出:'This is a comment'
type(newsoup.b.string)
输出:bs4.element.Comment
newsoup.p.string
输出:'This is not a comment'
type(newsoup.p.string)
输出:bs4.element.NavigableString

3.2 上行遍历,平行遍历,下行遍历
标签的遍历查询

for parent in soup.a.parents:
    if parent is None:
        print(parent)
    else:print(parent.name)
    输出:div
          div
          div
          body
          html
          [document]

平行遍历:平行遍历发生在同一个父亲节点下的各个子节点

soup.a.next_sibling
soup.a.previous_sibling

4. 信息标记
标记后的信息可形成信息组织结构,增加了信息的维度;标记后的信息可用于通信、存储或展示;标记的结构与信息一样具有重要价值;标记后的信息更利于程序理解和运用。
4.1 信息标记的三种形式
HTML的信息标记(hyper text markup language)
HTML是www(world wide web)的信息组织方式。
超文本(声音+图像+视频) + 文本

XML 扩展标记语言 Internet上的信息交互与传递,结构类似HTML。
结构类似:<img src = 'china.jpg' size = "10">...</img>
JSON 移动应用云端和节点的信息通信,无注释。
主要通过键值对表示。例如:“name”(键 key):"外国语中学"(值 value) (需要增加引号来表示字符串)
一个键对应多个值时:

“name”:["外国语中学",“私立中学”]    #多值情况,需要使用[,]表示

键值对的嵌套使用:

“name”:{
          "newname": "外国语中学",
          “oldname”: "私立中学"
              }    #键值对嵌套使用

YAML 各类系统的配置文件,有注释易读。
键值对表示,无类型:name(键 key):外国语中学(值 value) #不需要双引号
缩进关系表达嵌套所属关系:

name :
    newName : 外国语中学
    oldName : 私立中学    

YAML用-号表达并列关系:

name:
-外国语高中
-私立高中

|表示整块数据,#表示注释

key : value
key : #Comment
-value1
-value2
key :
    subkey : subvalue

4.2 信息提取的一般方法
方法一:完整解析信息的标记形式,再提取关键信息。
XML JSON YAML 需要标记解析器,例如bs4库的标签树遍历
优点 :信息解析准确 缺点 :提取过程繁琐,速度慢。
方法二:无视标记形式,直接搜索关键信息。
优点:提取过程简单,速度较快。
缺点:提取结果准确性与信息内容有关。
采取方法——两种方法融合
例如:提取HTML中所有URL链节
思路:1)搜索到所有标签。
2)解析标签格式,提取href后的链节内容。

<>.find_all(name, attrs, recursive, string, **kwargs)    #信息搜索方法

返回一个列表类型,村春查找结果。
name:对标签名称的检索字符串。

soup.find_all('a')    #返回所有a标签内容
soup.find_all(true)    #返回所有标签内容
import re
for tag in soup.find_all(re.compile('b')):
        print(tag.name)    #re为正则表达式,该代码返回包含b的所有标签名字

attrs:对标签属性值的检索字符串,可标注属性检索。

soup.find_all('p','course')    #返回具有course属性值的所有p标签
soup.find_all(id = 'link1')    #返回id属性中含有link1的标签元素
soup.find_all(id = 'link')    #需要精确描述属性值,只描述部分信息则返回为空

通过引入正则表达式,可通过寻找含有部分信息的属性或标签,即解决上述第三个程序问题

import re
soup.find_all(id = re.compile('link'))    #此时即可正常返回含有link1的标签元素

recursive:是否对子孙全部检索,默认True.
string:<>…</>中字符串区域的检索字符串。

soup.find_all(string = 'Basic python')   
['Basic Python']

利用正则表达式,通过检索某些特定字符将含有该字符的所有string搜索打印。

import re
soup.find_all(string = re.compile("Python"))
['This is a python demo page','the demo python introduce several python courses.']

一些简写方法:

<tag>(...)等价于<tag>.find_all(...)
soup(...)等价于soup.find_all(...)

5. 中国大学排名定向爬虫
功能描述:
输入:大学排名URL链节
输出:大学排名信息的屏幕输出(排名,大学名称,总分)
技术路线:requests-bs4
定向爬虫:仅针对输入URL进行爬取,不扩展爬取

程序的结构设计:
步骤一:从网络上获取大学排名网页内容 getHTMLText()
步骤二:提取网页内容信息到合适的数据结构(二位列表) fillUnivList()
步骤三:利用数据结构展示并输出结果 printUnivList()

程序代码:


    import requests
    from bs4 import BeautifulSoup
    import bs4        #引入库
    
    def getHTMLText(url):
        try:
            r = requests.get(url, timeout = 30)        #获取网页内容
            r.raise_for_status()        #异常状态查询
            r.encoding = r.apparent_encoding
            return r.text          #返回函数值
        except:
            return " "
    
    def fillUnivList(ulist, html):
        soup = BeautifulSoup(html, 'html.parser')        #美丽汤解析网页内容
        for tr in soup.find('tbody').children:
            if isinstance(tr, bs4.element.Tag):        #检验tr是否为标签格式,引入库bs4
                tds = tr('td')        #定义tr标签下td标签为tds
                ulist.append([tds[0].string,tds[1].string,tds[3].string])
    
    def printUnivList(ulist, num):
            print("{:^10}\t{:^6}\t{:^10}".format("排名“,”学校名称”,“总分“))        #打印一个表头
            for i in range(num):
                u=ulist[i]
                print("{:^10}\t{:^6}\t{:^10}".format(u[0], u[1], u[2]))        #将列表数据输入表格
     
     def main():
         uinfo = []
         url = 'http://www.zuihaodaxue.cn/zuihaodaxuepaiming2019.html'
         html = getHTMLText(url)
         fillUnivList(uinfo, html)
         printUnivList(uinfo, 20)        #输出前20所
    main()

输出结果的表格并不对齐,这里主要原因是因为中文字符与英文字符数长度不同导致,这里可以提前声明英文字符进行扩展。
首先介绍格式化输出字符含义:

字符含义
{}
^表示居中,后面数字表示槽长度
冒号前面应有数值(默认0,1,2),为format函数中参数

修改后程序为:

def printUnivList(ulist, num):
    tplt = "{0:^10}\t{1:{3}^10}\t{2:^10}"
    print(tplt.format("排名“,”学校名称”,“总分”,chr(12288))
    for i in range(num):
        u = ulist[i]
        print(tplt.format(u[0],u[1],u[2],chr(12288)

修改完成后,运行成功,结果如下图所示。
在这里插入图片描述

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值