文章目录
- Python爬虫学习笔记
- 一、安装并学习python的第三方库requests
- 二、网络爬虫“盗亦有道”(Robots协议学习)
- 三、Beautiful Soup库的安装及使用
- 四、Re(正则表达式)库入门
- 1.引入正则模块(Regular Expression)
- 2.主要使用的方法 match(), 从左到右进行匹配
- 3.正则表达式匹配规则
- 4.表示边界
- 5.匹配分组
- a.字符含义
- b.匹配出0-100之间的数字
- c. 从指定字符串开始操作
- d. 如果在 pattern 中捕获到括号,那么所有的组里的文字也会包含在列表里。如果 maxsplit 非零, 最多进行 maxsplit 次分隔, 剩下的字符全部返回到列表的最后一个元素
- e.如果分隔符里有捕获组合,并且匹配到字符串的开始,那么结果将会以一个空字符串开始。对于结尾也是一样
- f.这样的话,分隔组将会出现在结果列表中同样的位置。样式的空匹配将分开字符串,但只在不相临的状况生效
- g.如果一个组包含在样式的一部分,并被匹配多次,就返回最后一个匹配.
- h.如果正则表达式使用了 (?P…) 语法, groupN 参数就也可能是命名组合的名字。如果一个字符串参数在样式中未定义为组合名,一个 IndexError就 raise
- i.如果一个组匹配成功多次,就只返回最后一个匹配
- j.这个等价于 m.group(g)。这允许更方便的引用一个匹配
- k.Match.groups(*default=None*)返回一个元组,包含所有匹配的子组,在样式中出现的从1到任意多的组合。*default* 参数用于不参与匹配的情况,默认为None
- l.如果我们使小数点可选,那么不是所有的组都会参与到匹配当中。这些组合默认会返回一个 None ,除非指定了 default 参数
- m.Match.groupdict(default=None)返回一个字典,包含了所有的 命名 子组。key就是组名。default 参数用于不参与匹配的组合;默认为 None
- n.这个例子会从email地址中移除掉 remove_this
- o.findall() 匹配样式 所有 的出现,不仅是像 search() 中的第一个匹配。比如,如果一个作者希望找到文字中的所有副词,他可能会按照以下方法用 findall()
- 6.找到所有副词和位置
- 总结
Python爬虫学习笔记
代码审计中涉及到python爬虫,本周进行了专门的爬虫学习
学习的网课为中国大学生mooc上北京理工大学嵩天教授的国家精品课
一、安装并学习python的第三方库requests
1.下载并安装requests库
:requests库是python爬虫中 一种很方便实用的工具。
首先我们以管理员身份打开命令提示符并输入pip install requests,如图
2.requests库的get()方法
:requests.get(url,params=None,**kwargs)
{
url:拟获取页面的url链接
params:url的额外参数,字典或字节流格式,可选
**kwargs:12个控制访问的参数
}
使用requests库获取一个网页最简单的方法就是
r = requests.get(url)
requests.get(url)构造了一个向服务器请求资源的Request对象(这个对象是Requests库内部生成的),变量r用来表示返回的一个包含服务器资源的Response对象
具体操作一下(爬取百度主页源代码):
3.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
二、网络爬虫“盗亦有道”(Robots协议学习)
1.Robots协议
Robots协议,即Robots Exclusion Standard 网络爬虫排除协议。
作用:网站告知网络爬虫哪些页面可以爬取,哪些不能爬取
形式:在网站根目录下的robots.txt文件
2.Robots协议遵守的方式
编写的网络爬虫应自动或人工识别robots.txt,再进行其内容的爬取
Robots协议是建议但非约束性,网络爬虫可以不遵守,但存在法律风险
例:京东的Robots协议
http://www.jd.com/robots.txt
可以看到京东对爬虫的限制:
# 对于任何的网络爬虫来源,遵守如下协议
User-agent: *
Disallow: /?*
Disallow: /pop/*.html
Disallow: /pinpai/*.html?*
# 以下四个网络爬虫不允许爬取任何资源
User-agent: EtaoSpider
Disallow: /
User-agent: HuihuiSpider
Disallow: /
User-agent: GwdangSpider
Disallow: /
User-agent: WochachaSpider
Disallow: /
基本协议语法:
`# 注释
- 代表所有
./代表根目录
User-agent: * # 代表的是那些爬虫
Disallow: / # 代表不允许爬虫访问的目录
`
三、Beautiful Soup库的安装及使用
1.Beautiful Soup库的安装
:同样我们用管理员身份打开命令提示符并输入pip install beautifulsoup4,如图
2.Beautiful Soup库的基本使用
Beautiful Soup库,也叫beautifulsoup4或bs4、
使用该库时最常用的引用方式:from bs4 import BeautifulSoup
from bs4 import BeautifulSoup
BeautifulSoup对应一个HTML/XML文档的全部内容
我们进行实际操作一下:
首先我们先获取一个url链接的全部信息(使用requests库的get方法或者在网页中右键点击查看源代码)
演示HTML页面地址:http://python123.io/ws/demo.html
网页截图:
查看源代码:
此处我们用代码进行爬取
import requests
url = 'https://python123.io/ws/demo.html'
r = requests.get(url)
demo = r.text
print(demo)
此时我们就能得到这个url全部信息
<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>
此时我们使用BeautifulSoup库对其进行格式化的输出
from bs4 import BeautifulSoup
import requests
url = 'https://python123.io/ws/demo.html'
r = requests.get(url)
demo = r.text
soup = BeautifulSoup(demo, 'html.parser') # 对demo进行HTML的解析
print(soup.prettify()) # 格式化打印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 class="py1" href="http://www.icourse163.org/course/BIT-268001" id="link1">
Basic Python
</a>
and
<a class="py2" href="http://www.icourse163.org/course/BIT-1001870001" id="link2">
Advanced Python
</a>
.
</p>
</body>
</html>
3.基于bs4库的HTML内容遍历方法
我们将上面获取后经过格式化的html称为“标签树“
HTML基本格式:
把 HTML代码做一个结构化的设计,我们可以发现,这个demo配置,是一个具有树形结构的文本信息,里面有很多标签,标签标明了信息结构的逻辑关系。
标签树的下行遍历:
属性 | 说明 |
---|---|
.contents | 子节点的列表,将< tag >所有儿子节点存入列表 |
.children | 子节点的迭代类型,与.contents类似,用于循环遍历儿子节点 |
.descendants | 子孙节点的迭代类型,包含所有子孙节点,用于循环遍历 |
标签树的上行遍历:
属性 | 说明 |
---|---|
.parent | 节点的父亲标签 |
.parents | 节点先辈标签的迭代类型,用于循环遍历先辈节点 |
标签树的平行遍历:
属性 | 说明 |
---|---|
.next_sibling | 返回按照 HTML文本顺序的上一个平行节点标签 |
.previous_sibling | 返回按照 HTML文本顺序的上一个平行节点标签 |
.next_siblings | 迭代类型,返回按照 HTML文本顺序的后续所有平行节点标签 |
.previous_siblings | 迭代类型,返回按照 HTML文本顺序的前续所有平行节点标签 |
四、Re(正则表达式)库入门
1.引入正则模块(Regular Expression)
import re #引入正则表达式
2.主要使用的方法 match(), 从左到右进行匹配
result =re.match(pattern, str)
#pattern 为要校验的规则
#str 为要进行校验的字符串
>>> import re
>>> print(re.match('www', 'www.runoob.com').span()) #在起始位置匹配
(0, 3)
>>> print(re.match('com', 'www.runoob.com')) #不在起始位置匹配
None
#如果result不为None,则group方法则对result进行数据提取
3.正则表达式匹配规则
a.单字符串匹配规则
字符 功能
. 匹配任意1个字符(除了\n)
[] 匹配[]中列举的字符
\d 匹配数字,也就是0-9
\D 匹配非数字,也就是匹配不是数字的字符
\s 匹配空白符,也就是 空格\tab
\S 匹配非空白符,\s取反
\w 匹配单词字符, a-z, A-Z, 0-9, _
\W 匹配非单词字符, \w取反
b.表示数量的规则
字符 功能
* 匹配前一个字符出现0次多次或者无限次,可有可无,可多可少
+ 匹配前一个字符出现1次多次或则无限次,直到出现一次
? 匹配前一个字符出现1次或者0次,要么有1次,要么没有
{m} 匹配前一个字符出现m次
{m,} 匹配前一个字符至少出现m次
{m,n} 匹配前一个字符出现m到n次
c.案例:验证手机号码是否符合规则(不考虑边界问题)
#首先清楚手机号的规则
#1.都是数字 2.长度为11 3.第一位是1 4.第二位是35678中的一位
>>> import re
>>> pattern ="1[35678]\d{9}"
>>> phoneStr ="18230092223"
>>> result =re.match(pattern,phoneStr)
>>> result.group()
'18230092223'
4.表示边界
a.字符含义
字符 功能
^ 匹配字符串开头
$ 匹配字符串结尾
\b 匹配一个单词的边界
\B 匹配非单词边界
b.案例:边界(制定规则来匹配str=“ho ve r”)
#定义规则匹配str="ho ve r"
#1. 以字母开始 ^\w
#2. 中间有空字符 \s
#3. \b的两种解释是:
#'\b', 如果前面不加r, 那么解释器认为是转义字符“退格键backspace”;
#r'\b', 如果前面加r, 那么解释器不会进行转义,\b 解释为正则表达式模式中的字符串边界。
#4. ve两边分别限定匹配单词边界
>>> import re
>>> str ="dasdho ve rgsdf"
>>> pattern =r"^\w+\s\bve\b\sr"
>>> result =re.match(pattern, str)
>>> result.group()
'dasdho ve r'
5.匹配分组
a.字符含义
字符 功能
| 匹配左右任意一个表达式
(ab) 将括号中字符作为一个分组
\num 引用分组num匹配到的字符串
(?P<name>) 分组起别名
(?P=name) 引用别名为name分组匹配到的字符串
b.匹配出0-100之间的数字
#匹配出0-100之间的数字
#首先:正则是从左往又开始匹配
#经过分析: 可以将0-100分为三部分
#1. 0 "0$"
#2. 100 "100$"
#3. 1-99 "[1-9]\d{0,1}$"
#所以整合如下
>>> import re
>>> pattern =r"0$|100$|[1-9]\d{0,1}$"
>>> result = re.match(pattern,"27")
>>> result.group()
'27'
>>> result =re.match(pattern,"212")
>>> result.group()
Traceback (most recent call last):
File "<stdin>", line 1, in<module>
AttributeError: 'NoneType'object has no attribute 'group'
#将0考虑到1-99上,上述pattern还可以简写为:pattern=r"100$|[1-9]?\d{0,1}$"
c. 从指定字符串开始操作
#(?<=abc)def ,并不是从 a 开始搜索,而是从 d 往回看的。你可能更加愿意使用 search() 函数,而不是 match() 函数:
>>> import re
>>> m =re.search('(?<=abc)def', 'abcdef')
>>> m.group(0)
'def'
#搜索一个跟随在连字符后的单词
>>> m =re.search(r'(?<=-)\w+', 'spam-egg')
>>> m.group(0)
'egg'
d. 如果在 pattern 中捕获到括号,那么所有的组里的文字也会包含在列表里。如果 maxsplit 非零, 最多进行 maxsplit 次分隔, 剩下的字符全部返回到列表的最后一个元素
>>> re.split(r'\W+', 'Words, words, words.')
['Words', 'words', 'words', '']
>>> re.split(r'(\W+)', 'Words, words, words.')
['Words', ', ', 'words', ', ', 'words', '.', '']
>>> re.split(r'\W+', 'Words, words, words.', 1)
['Words', 'words, words.']
>>> re.split('[a-f]+', '0a3B9', flags=re.IGNORECASE)
['0', '3', '9']
e.如果分隔符里有捕获组合,并且匹配到字符串的开始,那么结果将会以一个空字符串开始。对于结尾也是一样
>>> re.split(r'(\W+)', '...words, words...')
['', '...', 'words', ', ', 'words', '...', '']
f.这样的话,分隔组将会出现在结果列表中同样的位置。样式的空匹配将分开字符串,但只在不相临的状况生效
>>> re.split(r'\b', 'Words, words, words.')
['', 'Words', ', ', 'words', ', ', 'words', '.']
>>> re.split(r'\W*', '...words...')
['', '', 'w', 'o', 'r', 'd', 's', '', '']
>>> re.split(r'(\W*)', '...words...')
['', '...', '', '', 'w', '', 'o', '', 'r', '', 'd', '', 's', '...', '', '', '']
g.如果一个组包含在样式的一部分,并被匹配多次,就返回最后一个匹配.
>>> m =re.match(r"(\w+) (\w+)", "Isaac Newton, physicist")
>>> m.group(0) # The entire match
'Isaac Newton'
>>> m.group(1) # The first parenthesized subgroup.
'Isaac'
>>> m.group(2) # The second parenthesized subgroup.
'Newton'
>>> m.group(1, 2) # Multiple arguments give us a tuple.
('Isaac', 'Newton')
h.如果正则表达式使用了 (?P…) 语法, groupN 参数就也可能是命名组合的名字。如果一个字符串参数在样式中未定义为组合名,一个 IndexError就 raise
>>> m =re.match(r"(?P<first_name>\w+) (?P<last_name>\w+)", "Malcolm Reynolds")
>>> m.group('first_name')
'Malcolm'
>>> m.group('last_name')
'Reynolds'
#命名组合同样可以通过索引值引用
>>> m.group(1)
'Malcolm'
>>> m.group(2)
'Reynolds'
i.如果一个组匹配成功多次,就只返回最后一个匹配
#匹配最后两位
>>> m =re.match(r"(..)+", "a1b2c3") # Matches 3 times.
>>> m.group(1) # Returns only the last match.
'c3'
j.这个等价于 m.group(g)。这允许更方便的引用一个匹配
#匹配单词字符每个代表一个字符,一共匹配了两个
>>> m =re.match(r"(\w+) (\w+)", "Isaac Newton, physicist")
>>> m[0] # The entire match
'Isaac Newton'
>>> m[1] # The first parenthesized subgroup.
'Isaac'
>>> m[2] # The second parenthesized subgroup.
'Newton'
k.Match.groups(default=None)返回一个元组,包含所有匹配的子组,在样式中出现的从1到任意多的组合。default 参数用于不参与匹配的情况,默认为None
>>> m =re.match(r"(\d+)\.(\d+)", "24.1632")
>>> m.groups()
('24', '1632')
l.如果我们使小数点可选,那么不是所有的组都会参与到匹配当中。这些组合默认会返回一个 None ,除非指定了 default 参数
>>> m =re.match(r"(\d+)\.?(\d+)?", "24")
>>> m.groups() # Second group defaults to None.
('24', None)
>>> m.groups('0') # Now, the second group defaults to '0'.
('24', '0')
m.Match.groupdict(default=None)返回一个字典,包含了所有的 命名 子组。key就是组名。default 参数用于不参与匹配的组合;默认为 None
>>> m =re.match(r"(?P<first_name>\w+) (?P<last_name>\w+)", "Malcolm Reynolds")
>>> m.groupdict()
{'first_name': 'Malcolm', 'last_name': 'Reynolds'}
n.这个例子会从email地址中移除掉 remove_this
>>> email ="tony@tiremove_thisger.net"
>>> m =re.search("remove_this", email)
>>> email[:m.start()] +email[m.end():]
'tony@tiger.net'
o.findall() 匹配样式 所有 的出现,不仅是像 search() 中的第一个匹配。比如,如果一个作者希望找到文字中的所有副词,他可能会按照以下方法用 findall()
>>> text ="He was carefully disguised but captured quickly by police."
>>> re.findall(r"\w+ly", text)
['carefully', 'quickly']
6.找到所有副词和位置
如果需要匹配样式的更多信息, finditer() 可以起到作用,它提供了 匹配对象作为返回值,而不是字符串。继续上面的例子,如果一个作者希望找到所有副词和它的位置,可以按照下面方法使用 finditer()
>>> text ="He was carefully disguised but captured quickly by police."
>>> form inre.finditer(r"\w+ly", text):
... print('%02d-%02d: %s'% (m.start(), m.end(), m.group(0)))
07-16: carefully
40-47: quickly
总结
爬虫多实践,注意robots协议,触网即有痕,注重《网络空间安全法》