爬虫学习笔记(1)

爬虫入门的学习比我想象的要简单,目前已经掌握了固定网页的定向爬取。

网页爬取

Requests库的7个主要方法

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

get()和head()对于普通用户来说是最常用的。

requests.request()
requests.request(method, url, **kwargs)

method: 请求方式,包括7种
GET HEAD POST PUT PATCH delete OPTIONS

**kwargs: 控制访问的参数,均为可选项
#重点掌握
params: 字典或字节序列,作为参数增加到url中,对url进行修改
data: 字典、字节序列、文件对象,作为Request的内容(作为向服务器提交资源时使用)
json: JSON格式的数据,作为Request的内容,用法同data参数
headers: 字典,“定制访问某个URL的HTTP的协议头”

cookies: 字典或CookieJar,Request中的cookie。“从HTTP协议中解析cookie”
auth: 元组,支持HTTP认证功能
files: 字典,向服务器传输文件
timeout: 设定超时时间,秒为单位。请求的内容超时了还没有返回,就会返回一个timeout异常
proxies: 字典类型,设定访问代理服务器,可以增加登录认证(隐藏用户爬取网页原的IP地址信息)

#开关字段
allow_redirects: True/False, 默认为True, 重定向开关
stream: True/False, 默认为True, 获取内容立即下载开关
verify: T/F,默认为T,认证SSL证书开关

cert: 保存本地SSL证书路径

用法举例

#params
kv={"key1": "value1", "key2": "value2"}
r=requests.request("GET", "http://python123.io/ws", params=kv)
print(r.url)
https://python123.io/ws?key1=value1&key2=value2

#data
#同下文requests.post()的用法

#headers
hd={"user-agent":"chrome/10"}
r=requests.request("POST", "http://python123.io/ws", headers=hd)
##模拟chrome v10浏览器向服务器发起访问

#files
fs={"file": open("tmp.xlsx", "rb")}
r=requests.request("POST", "http://python123.io/ws", files=fs)

#proxies
pxs={'http': 'http://user:pass@10.10.10.1:1234', 'https': 'http://10.10.10.1:4321'}
r=requests.request("GET", "http://www.baidu.com", proxies=pxs)
requests.get()

requests库最常用方法

#标准用法:
#requests.get(url, params=None, **kwargs)
#r=requests.get(url)
#右边构造一个向服务器请求资源的Requests对象,此对象是Requests库内部生成的
#左边是返回的一个包含服务器所有相关资源的Response对象
r=requests.get("http://m.dict.cn/msearch.php?q=request")
r.status_code #状态码200为连接成功
r.encodeing="utf-8"
r.text
requests.head()

当资源很大时,用head()方法获得资源概要

#标准用法:
#requests.head(url, **kwargs)
r=requests.head("http://www.chinadaily.com.cn/a/201910/01/WS5d92b7a1a310cf3e3556e759.html")
r.headers
requests.post()
#标准用法:
#requests.post(url,data=None,json=None,**kwargs)
test_dict={"user_name": "hsy", "user_age": "23"}

r=requests.post("http://httpbin.org/post", data=test_dict)

print(r.text)
{
  "args": {}, 
  "data": "", 
  "files": {}, 
  "form": {
    "user_age": "23", 
    "user_name": "hsy"
  }, 
  "headers": {
    "Accept": "*/*", 
    "Accept-Encoding": "gzip, deflate", 
    "Content-Length": "25", 
    "Content-Type": "application/x-www-form-urlencoded", 
    "Host": "httpbin.org", 
    "User-Agent": "python-requests/2.22.0"
  }, 
  "json": null, 
  "origin": "115.27.203.85, 115.27.203.85", 
  "url": "https://httpbin.org/post"
}

向URL POST一个字典, 自动编码为form

r=requests.post("http://httpbin.org/post", data="huangsiyuan")

print(r.text)
{
  "args": {}, 
  "data": "huangsiyuan", 
  "files": {}, 
  "form": {}, 
  "headers": {
    "Accept": "*/*", 
    "Accept-Encoding": "gzip, deflate", 
    "Content-Length": "11", 
    "Host": "httpbin.org", 
    "User-Agent": "python-requests/2.22.0"
  }, 
  "json": null, 
  "origin": "115.27.203.85, 115.27.203.85", 
  "url": "https://httpbin.org/post"
}

追加字符串则是在"data"中

requests.put()

与上面类似,只是有覆盖操作

#标准用法:
requests.put(url,data=None,**kwargs)
requests.patch()
requests.patch(url,data=None,**kwargs)
requests.delete()
requests.delete(url,**kwargs)

Response对象的属性

r.status_code HTTP请求的返回状态,200表示连接成功,404(只要不是200)表示失败
r.text url对应的页面内容
r.encodeing 从HTTP header中猜测页面内容的编码方式
r.apparent_encoding 从内容文本中分析出编码方式(备选编码,如果文本中有中文,需将r.apparent_encoding赋值给r.encodeing)
r.content 页面内容的二进制形式

Requests库的异常

requests.ConnectTimeout 连接远程服务器超时(一个预定时间)异常
requests.ConnectionError    网络连接错误异常,如DNS查询失败、(服务器防火墙)拒绝连接等
requests.HTTPError  HTTP错误异常
requests.URLRequired    URL缺失异常
requests.TooManyRedirects   超过最大重定向次数,产生重定向异常
requests.Timeout    请求URL(到获得返回内容整个过程)超时,产生超时异常

爬取网页的通用代码框架

r.raise_for_status() 如果状态码不是200就会爆出requests.HTTPError,利用这个特性可以放在代码中检验
Python中异常处理用到try except

import requests

def getHTMLText(url):
    try:
        r=requests.get(url, timeout=30)
        r.raise_for_status()
        r.encoding=r.apparent_encoding
        return r.text
    except:
        return "产生异常"
if __name__ == "__main__":
    url="http://www.chinadaily.com.cn/"
    print(getHTMLText(url))

HTTP协议

url格式
#http://host[:port][path]
#host: 合法的Internet主机`域名`或`IP地址`
#port: 端口号,缺省端口为80
#path: 请求资源的内部路径

http://www.chinadaily.com.cn/a/201910/01/WS5d92b7a1a310cf3e3556e759.html

如何理解URL

URL是通过HTTP协议存取资源的Internet路径,一个URL对应一个数据资源。好比电脑上一个文件(夹)路径对应一个文件(夹)。

HTTP协议对资源的操作
GET 请求获取URL的资源
HEAD 请求获取URL位置资源的头部信息
POST 请求向URL位置的资源后附加新的数据
PUT 请求向URL位置存储一个资源,覆盖原URL位置的资源
PATCH 改变该处资源的部分内容
DELETE 请求删除URL位置存储的资源

与Requests库中的6个方法一致

关于网络爬虫

网络爬虫的尺寸

爬取网页 Requests库 占比90%
爬取网站 Scrapy库
爬取全网(为了建立搜索引擎,如百度,谷歌背后的爬虫)

网络爬虫的限制
  • 来源审查:判断User-Agent进行限制
    检查来访HTTP协议头的User-Agent域,只响应浏览器或友好爬虫的访问
  • 发布公告:Robots协议
    告知所有爬虫网站的爬取策略,要求爬虫遵守
Robots协议

作用:网站告知网络爬虫哪些页面可以爬取,哪些不行
形式:在网站根目录下的robots.txt文件
使用

  • 网络爬虫:自动或人工识别robots.txt,再进行内容爬取
  • 约束性:商业用途格外小心;可以不遵守但存在法律风险

类人行为可不参考Robots协议,比如一天只访问一个网页几次

网页解析

Beautiful Soup

HTML格式

例子

<html><head><title>This is a python demo page</title></head><body><p class="title"><b>The demo python introduces several python courses.</b></p><p class="course">Python is a wonderful general-purpose programming language. You can learn Python from novice to professional by tracking the following courses:<a href="http://www.icourse163.org/course/BIT-268001" class="py1" id="link1">Basic Python</a> and <a href="http://www.icourse163.org/course/BIT-1001870001" class="py2" id="link2">Advanced Python</a>.</p></body></html>

标签的基本结构

基本用法
import requests
#课程的例子
#r=requests.get("https://www.python123.io/ws/demo.html")
#在海词词典网页版上面查询单词:prologue。url如下,判断接口的什么样子。
r=requests.get("http://m.dict.cn/msearch.php?q=prologue")
r.status_code
r.text

demo=r.text #此时demo是一个html格式的信息

from bs4 import BeautifulSoup #从库中引入一个类
#在提供了demo, 解析器之后生成一个名叫soup的对象
soup=BeautifulSoup(demo,"html.parser")
print(soup.prettify())

最终可以解析成树形结构

或者直接从HTML文件开始解析

理解:HTML文档,标签树,BeautifulSoup类三者等价

BeautifulSoup库的解析器
解析器 用法  条件
bs4的HTML解析器 BeautifulSoup(mk,'html.parser') 安装bs4库
lxml的HTML解析器    BeautifulSoup(mk,'lxml')    安装lxml
lxml的XML解析器 BeautifulSoup(mk,'xml') 安装lxml
html5lib的解析器    BeautifulSoup(mk,'html5lib')    安装html5lib
BeautifulSoup类的基本元素

获取标签内容
任何符合HTML语法的标签都可以通过类似soup.tag获取

>>> soup.strong
<strong>开场白;序言;序幕</strong>

当相同名称的标签有多个时,上述用法会返回第一个

>>> soup.ol
<ol>
<li>序言</li>
<li>开场白</li>
<li>序幕</li>
<li>序诗</li>
<li>序幕性事件</li>
<li>序</li>
<li>开端</li>
<li>作开场白的演员</li>
</ol>
>>> soup.ol.li
<li>序言</li>

Name

>>> soup.strong.name
'strong'
>>> soup.strong.parent.name
'li'

属性

>>> soup.div.attrs
{'class': ['tm']}
#查看具体的value
>>> soup.div.attrs['class']
['tm']

标签内部的字符串

>>> soup.strong.string
'开场白;序言;序幕'

注释
在HTML格式中,代表注释信息

基于bs4库的HTML网页内容遍历方法

标签树的下行遍历

.contents   子节点的列表,将<tag>所有F1节点存入列表(在列表中不只有tag,还可能有"\n"等字符串)
.children   子节点的迭代类型,与.content类似,用于循环遍历F1节点
.descendants    子孙节点的迭代类型,包含所有子孙节点,用于循环遍历

#后面两个用法类似
for child in soup.ol.children:
    print(child)

标签树的上行遍历

.parent 节点的父亲标签
.parents    节点先辈标签的迭代类型,用于循环遍历先辈节点

for parent in soup.strong.parents:
    if parent is None:
        print(parent)
    else:
        print(parent.name)

标签树的平行遍历

.next_sibling   返回按照HTML文本顺序的下一个平行节点标签(可能会返回字符串,如"\n")
.previous_sibling   返回按照HTML文本顺序的上一个平行节点标签
.next_siblings  迭代类型,返回按照HTML文本顺序的后续所有平行节点标签
.previous_siblings  迭代类型,返回按照HTML文本顺序的前续所有平行节点标签
基于bs4库的HTML格式输出

prettify()方法为标签树各个层次更友好地输出

信息标记和提取

信息标记的3种方法

XML(HTML属于此类) 最早的通用信息标记语言,可扩展性好,但繁琐;Internet上的信息交互与传递
JSON 信息有类型,适合程序处理(js),较XML简洁;移动应用云端和节点的信息通信,程序对接口的处理,无注释
YAML 信息无类型,文本信息比例最高,可读性好;各类系统的配置文件,有注释易读

信息提取的一般方法

如何从已标记的信息中提取想要的部分?
方法一
完整解析信息的标记形式(3种),再提取关键信息
需要标记解析器,如:bs4库的标签树遍历
优点:准确
缺点:繁琐,耗时
方法二
无视标记形式,直接搜索关键信息
对信息的文本使用查找函数即可
优点:简洁,快
缺点:提取准确性与信息内容复杂程度相关
融合方法
需要标记解析器及文本查找函数

实例
提取HTML中所有URL链接
思路:

  1. 搜索所有<a>标签
  2. 解析<a>标签格式,提取href后的链接内容
for link in soup.find_all('a'):
    print(link.get('href'))
基于bs4库的HTML内容查找方法
.find_all(name, attrs, recursive, string, **kwargs) 返回一个列表类型,存储查找的结果

name: 对标签名称的检索字符串
soup.find_all('strong') 寻找strong标签
for tag in soup.find_all(True):print(tag.name)  打印所有标签名称

attrs: 对标签属性值的检索字符串,可标注属性检索
soup.find_all('div','cl')
soup.find_all(id="dbann") #此时是精确查找
soup.find_all(id=re.compile('d')) #正则查找

recursive: 是否对子孙全部搜索,默认为True
soup.find_all('li',recursive=False)

string: <>...</>中字符串区域的检索字符串
soup.find_all(string="海词词典") #此时是精确查找
soup.find_all(string=re.compile("海词"))

综上:正则表达式+find_all()
简写形式

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

拓展方法
参数同find_all(), 不一定常用

.find() 搜索且只返回一个结果,字符串类型
.find_parents() 在先辈节点中搜索,返回列表类型
.find_parent()  在先辈节点中返回一个结果,字符串类型
.find_next_siblings()   在后续平行节点中搜索,返回列表类型
.find_next_sibling()    在后续平行节点中搜索,字符串类型
.find_previous_siblings()   在前续平行节点中搜索,返回列表类型
.find_previous_sibling()    在前续平行节点中返回一个结果,字符串类型

前面都需要接tag标签,如果是一个具体的bs对象,比如上文的soup,则相当于根节点,查找时不包括当前节点。

print()的格式化输出

print("{:^10}\t{:>6}\t{:6}\t{:^10}".format("排名","学校名称","地区","总分"))

#每一个槽从左到右可以设置哪些东西
:   引导符号
<填充>    用于填充的单个字符
<对齐>    <左对齐    >右对齐    ^居中对齐
<宽度>    槽的设定输出宽度
,   数字的千位分隔符适用于整数和浮点数
<.精度>   浮点数小数部分的精度或者字符串的最大输出长度
<类型>    整数类型b,c,d,o,x,X  浮点数类型e,E,f,%

tplt="{0:^10}\t{1:{4}^10}\t{2:{4}^10}\t{3:^10}"
print(tplt.format("排名","学校名称","地区","总分",chr(12288)))
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,132评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,802评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,566评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,858评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,867评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,695评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,064评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,705评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,915评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,677评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,796评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,432评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,041评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,992评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,223评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,185评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,535评论 2 343

推荐阅读更多精彩内容