目录导航:
文章目录
- 七、Python网络爬虫基础(上)
- 八、Python网络爬虫基础(下)
七、Python网络爬虫基础(上)
1. Python中的正则表达式
-
一些表达式进行提取,正则表达式就是其中一种进行数据筛选的表达式。
-
正则表达式(Regular Expression)
是一种文本模式,包括普通字符(例如,a 到 z 之间的字母)和特殊字符(称为"元字符")。 -
正则表达式通常被用来
匹配
、检索
、替换
和分割
那些符合某个模式(规则)的文本。 -
Python
自1.5版本
起增加了re
模块,它提供Perl风格
的正则表达式模式。 -
re
模块使Python
语言拥有全部的正则表达式功能,使用前需要使用import re
导入此模块-
compile
函数根据一个模式字符串和可选的标志参数生成一个正则表达式对象。该对象拥有一系列方法用于正则表达式匹配和替换。import re str = 'This year is 2018' pat = re.compile("[0-9]{4}") print(pat.findall(str)) #结果:['2018']
-
re
模块也提供了与这些方法功能完全一致的函数,这些函数使用一个模式字符串做为它们的第一个参数。import re str = 'This year is 2018' res = re.findall("[0-9]{4}",str) print(res) #结果:['2018']
-
2. 正则表达式基础语法介绍
2.1 原子
- 原子是正则表达式中最基本的组成单位,每个正则表达式中至少包含一个原子。
- 常见的原子类型有:
- 普通字符作为原子 如:a b c 字母
- 非打印字符作为原子 如:\n \t
- 通用字符作为原子 如:\d \D \w \W \s \S
- 原子表 如:多个原子拼接在一起
非打印字符:
字符 | 描述 |
---|---|
\cx | 匹配由x指明的控制字符。例如, \cM 匹配一个 Control-M 或回车符。 x 的值必须为 A-Z 或 a-z 之一。否则,将 c 视为一个原义的 ‘c’ 字符。 |
\f | 匹配一个换页符。等价于 \x0c 和 \cL。 |
\n | 匹配一个换行符。等价于 \x0a 和 \cJ。 |
\r | 匹配一个回车符。等价于 \x0d 和 \cM。 |
\s | 匹配任何空白字符,包括空格、制表符、换页符等等。等价于 [ \f\n\r\t\v] 。 |
\S | 匹配任何非空白字符。等价于 [^ \f\n\r\t\v] 。 |
\t | 匹配一个制表符。等价于 \x09 和 \cI。 |
\v | 匹配一个垂直制表符。等价于 \x0b 和 \cK。 |
通用字符:
字符 | 描述 |
---|---|
\d | 匹配一个数字字符。等价于[0-9] 。 |
\D | 匹配一个非数字字符。等价于[^0-9] 。 |
\s | 匹配任何空白字符,包括空格、制表符、换页符等等。等价于 [\f\n\r\t\v] 。 |
\S | 匹配任何非空白字符。等价于 [^ \f\n\r\t\v] 。 |
\w | 匹配字母、数字、下划线。等价于'[A-Za-z0-9_]' 。 |
\W | 匹配非字母、数字、下划线。等价于 '[^A-Za-z0-9_]' 。 |
2.2 元字符
- 所谓的元字符,就是正则表达式中具有一些特殊含义的字符,比如重复N次前面的字符等。
元字符:
字符 | 描述 |
---|---|
. | 匹配除 “\n” 之外的任何单个字符。 要匹配包括 ‘\n’ 在内的任何字符,请使用像"(.¦\n)"的模式。 |
[xyz] | 字符集合。匹配所包含的任意一个字符。例如, ‘[abc]’ 可以匹配 “plain” 中的 ‘a’。 |
[^xyz] | 负值字符集合。匹配未包含的任意字符。例如, '[^abc]' 可以匹配 “plain” 中的’p’、‘l’、‘i’、‘n’。 |
[a-z] | 字符范围。匹配指定范围内的任意字符。 例如,’[a-z]’ 可以匹配 ‘a’ 到 ‘z’ 范围内的任意小写字母字符。 |
[^a-z] | 负值字符范围。匹配任何不在指定范围内的任意字符。 例如,'[^a-z]' 可以匹配任何不在 ‘a’ 到 'z’范围内的任意字符。 |
* | 匹配前面的子表达式零次或多次。例如,zo 能匹配 “z” 以及 “zoo”。 等价于{0,}。 |
+ | 匹配前面的子表达式一次或多次。 例如,‘zo+’ 能匹配 “zo” 以及 “zoo”,但不能匹配 “z”。+ 等价于 {1,}。 |
? | 匹配前面的子表达式零次或一次。例如,“do(es)?” 可以匹配 “do” 或 “does” 。? 等价于 {0,1}。 |
{n} | n 是一个非负整数。匹配确定的 n 次。 例如,‘o{2}’ 不能匹配 “Bob” 中的 ‘o’,但是能匹配 “food” 中的两个 o。 |
{n,} | n 是一个非负整数。至少匹配n 次。例如,‘o{2,}’ 不能匹配 “Bob” 中的 ‘o’,但能匹配 “foooood” 中的所有 o。‘o{1,}’ 等价于 ‘o+’。‘o{0,}’ 则等价于 ‘o*’。 |
{n,m} | m 和 n 均为非负整数,其中n <= m。最少匹配 n 次且最多匹配 m 次。例如,“o{1,3}” 将匹配 “fooooood” 中的前三个 o。‘o{0,1}’ 等价于 ‘o?’。请注意在逗号和两个数之间不能有空格。 |
\ | 将下一个字符标记为一个特殊字符、或一个原义字符、或一个向后引用、或一个八进制转义符。例如,‘n’ 匹配字符 “n”。’\n’ 匹配一个换行符。序列 ‘’ 匹配 “” 而 “(” 则匹配 “(”。 |
^ | 匹配输入字符串的开始位置。如果设置了 RegExp 对象的 Multiline 属性,^ 也匹配 ‘\n’ 或 ‘\r’ 之后的位置。 |
$ | 匹配输入字符串的结束位置。如果设置了RegExp 对象的 Multiline 属性,$ 也匹配 ‘\n’ 或 ‘\r’ 之前的位置。 |
? | 当该字符紧跟在任何一个其他限制符 (*, +, ?, {n}, {n,}, {n,m}) 后面时,匹配模式是非贪婪的。非贪婪模式尽可能少的匹配所搜索的字符串,而默认的贪婪模式则尽可能多的匹配所搜索的字符串。例如,对于字符串 “oooo”,‘o+?’ 将匹配单个 “o”,而 ‘o+’ 将匹配所有 ‘o’。 |
(pattern) | 匹配 pattern 并获取这一匹配。所获取的匹配可以从产生的 Matches 集合得到,在VBScript 中使用 SubMatches 集合,在JScript 中则使用 $0…$9 属性。要匹配圆括号字符,请使用 ‘(’ 或 ‘)’。 |
(?:pattern) | 匹配 pattern 但不获取匹配结果,也就是说这是一个非获取匹配,不进行存储供以后使用。这在使用 “或” 字符 (¦) 来组合一个模式的各个部分是很有用。例如, 'industr(?:y¦ies) 就是一个比’industry¦industries’更简略的表达式。 |
x¦y | 匹配 x 或 y。例如,‘z¦food’ 能匹配 “z” 或 “food”。’(z¦f)ood’ 则匹配 “zood” 或 “food”。 |
import re
rst = re.rearch('',str)
2.3 模式修正符
- 所谓模式修正符,即可以在不改变正则表达式的情况下,通过模式修正符改变正则表达式的含义,从而实现一些匹配结果的调整等功能。
修饰符 | 描述 |
---|---|
re.I | 使匹配对大小写不敏感 |
re.L | 做本地化识别(locale-aware)匹配 |
re.M | 多行匹配,影响 ^ 和 $ |
re.S | 使 . 匹配包括换行在内的所有字符 |
re.U | 根据Unicode字符集解析字符。这个标志影响 \w, \W, \b, \B. |
re.X | 该标志通过给予你更灵活的格式以便你将正则表达式写得更易于理解。 |
- 实例:
string = "Python"
pat = "pyt"
rst = re.search(pat,string,re.I) # 第三个参数
print(rst)
2.4 贪婪模式与懒惰模式
- 贪婪模式的核心点就是尽可能多的匹配,而懒惰模式的核心点就是尽可能少的匹配。
.*? 的使用
3. 正则表达式中的常用函数
- 正则表达式本身是一种小型的、高度专业化的编程语言,而在python中,通过内嵌集成
re
模块来实现正则匹配。 - re模块中常用功能函数:
3.1 compile():
-
编译正则表达式模式,返回一个
正则对象
的模式。(可以把那些常用的正则表达式编译成正则表达式对象,这样可以提高一点效率。) -
格式:
re.compile(pattern[,flags=0])
pattern
: 编译时用的表达式字符串。flags
: 编译标志位,用于修改正则表达式的匹配方式,如:re.I(不区分大小写)、re.S等
import re
tt = "Tina is a good girl, she is cool, clever, and so on..."
rr = re.compile(r'\w*oo\w*')
print(rr.findall(tt)) #查找所有包含'oo'的单词
# 执行结果如下:
# ['good', 'cool']
3.2 match()
- 决定RE是否在字符串刚开始的位置匹配。
- //注:这个方法并不是完全匹配。当pattern结束时若string还有剩余字符,仍然视为成功。
- 格式:
re.match(pattern, string[, flags=0])
print(re.match('com','comwww.csdn').group())
print(re.match('com','Comwww.csdn',re.I).group())
#执行结果如下:
#com
#com
3.3 search()
- 格式:
re.search(pattern, string[, flags=0])
- re.search函数会在字符串内查找模式匹配,只要找到第一个匹配然后返回,如果字符串没有匹配,则返回None。
print(re.search('\dcom','www.4comcsdn.5com').group())
执行结果如下:
# 4com
- 注:match和search一旦匹配成功,就是一个match object对象,而match object对象有以下方法:
- group() 返回被 RE 匹配的字符串
- start() 返回匹配开始的位置
- end() 返回匹配结束的位置
- span() 返回一个元组包含匹配 (开始,结束) 的位置
import re
a = "123abc456"
print(re.search("([0-9]*)([a-z]*)([0-9]*)",a).group(0)) #123abc456,返回整体
print(re.search("([0-9]*)([a-z]*)([0-9]*)",a).group(1)) #123
print(re.search("([0-9]*)([a-z]*)([0-9]*)",a).group(2)) #abc
print(re.search("([0-9]*)([a-z]*)([0-9]*)",a).group(3)) #456
###group(1) 列出第一个括号匹配部分,group(2) 列出第二个括号匹配部分,group(3) 列出第三个括号匹配部分。###
3.4 findall()
- re.findall遍历匹配,可以获取字符串中所有匹配的字符串,返回一个列表。
- 格式:
re.findall(pattern, string[, flags=0])
p = re.compile(r'\d+')
print(p.findall('o1n2m3k4'))
执行结果如下:
['1', '2', '3', '4']
import re
tt = "Tina is a good girl, she is cool, clever, and so on..."
rr = re.compile(r'\w*oo\w*')
print(rr.findall(tt))
print(re.findall(r'(\w)*oo(\w)',tt))#()表示子表达式
执行结果如下:
['good', 'cool']
[('g', 'd'), ('c', 'l')]
3.5 finditer()
- 搜索string,返回一个顺序访问每一个匹配结果(Match对象)的迭代器。
- 找到 RE 匹配的所有子串,并把它们作为一个迭代器返回。
- 格式:
re.finditer(pattern, string[, flags=0])
iter = re.finditer(r'\d+','12 drumm44ers drumming, 11 ... 10 ...')
for i in iter:
print(i)
print(i.group())
print(i.span())
'''
# 执行结果如下:
<_sre.SRE_Match object; span=(0, 2), match='12'>
12
(0, 2)
<_sre.SRE_Match object; span=(8, 10), match='44'>
44
(8, 10)
<_sre.SRE_Match object; span=(24, 26), match='11'>
11
(24, 26)
<_sre.SRE_Match object; span=(31, 33), match='10'>
10
(31, 33)
'''
3.6 split()
-
按照能够匹配的子串将string分割后返回列表。
-
可以使用re.split来分割字符串,如:re.split(r’\s+’, text);将字符串按空格分割成一个单词列表。
-
格式:
re.split(pattern, string[, maxsplit])
maxsplit
: 用于指定最大分割次数,不指定将全部分割。
print(re.split('\d+','one1two2three3four4five5'))
# 执行结果如下:
# ['one', 'two', 'three', 'four', 'five', '']
3.7 sub()
- 使用re替换string中每一个匹配的子串后返回替换后的字符串。
- 格式:
re.sub(pattern, repl, string, count)
import re
text = "JGood is a handsome boy, he is cool, clever, and so on..."
print(re.sub(r'\s+', '-', text))
执行结果如下:
JGood-is-a-handsome-boy,-he-is-cool,-clever,-and-so-on...
其中第二个函数是替换后的字符串;本例中为'-'
第四个参数指替换个数。默认为0,表示每个匹配项都替换。
- re.sub还允许使用函数对匹配项的替换进行复杂的处理。
- 如:re.sub(r’\s’, lambda m: ‘[’ + m.group(0) + ‘]’, text, 0);将字符串中的空格’ ‘替换为’[ ]’。
import re
text = "JGood is a handsome boy, he is cool, clever, and so on..."
print(re.sub(r'\s+', lambda m:'['+m.group(0)+']', text,0))
执行结果如下:
JGood[ ]is[ ]a[ ]handsome[ ]boy,[ ]he[ ]is[ ]cool,[ ]clever,[ ]and[ ]so[ ]on...
3.8 subn()
- 返回替换次数
- 格式:
subn(pattern, repl, string, count=0, flags=0)
print(re.subn('[1-2]','A','123456abcdef'))
print(re.sub("g.t","have",'I get A, I got B ,I gut C'))
print(re.subn("g.t","have",'I get A, I got B ,I gut C'))
执行结果如下:
('AA3456abcdef', 2)
I have A, I have B ,I have C
('I have A, I have B ,I have C', 3)
3.9 re.match与re.search与re.findall的区别:
- re.match只匹配字符串的开始,如果字符串开始不符合正则表达式,则匹配失败,函数返回None;
- 而re.search匹配整个字符串,直到找到一个匹配。
a=re.search('[\d]',"abc33").group()
print(a)
p=re.match('[\d]',"abc33")
print(p)
b=re.findall('[\d]',"abc33")
print(b)
执行结果:
3
None
['3', '3']
4. 正则表达式的案例实战
4.1 正则表达式在Python中的使用–解析数据
- 案例要求:如下图所示有一个网页文件,请使用Python的正则将网页中的超级链接信息(名称和url地址)解析处理
- 实现步骤:使用open()、read()读取文件内容,导入re模块,使用正则匹配后遍历输出。
<!DOCTYPE html>
<html>
<head>
<title>Python 正则表达式实例</title>
</head>
<body>
<h2>常用网站链接</h2>
<ul>
<li><a href="https://www.python.org">Python官方网站</a></li>
<li><a href="https://www.djangoproject.com">Django官方网站</a></li>
<li><a href="https://www.baidu.com">百度搜索引擎</a></li>
<li><a href="https://blog.csdn.net">CSDN官方网站</a></li>
<li><a href="https://edu.csdn.net/">CSDN学院</a></li>
</ul>
</body>
</html>
- 实现代码:
import re
f = open("./index.html","r")
content = f.read()
f.close()
#print(content)
title = re.search("<title>(.*?)</title>",content)
if title:
print(title)
print("标题:"+title.group())
alist = re.findall('<a href="(.*?)">(.*?)</a>',content)
for ov in alist:
print(ov[1]+":"+ov[0])
`
4.2 正则表达式在JavaScript中的使用–表单验证
- 案例要求:参考下图,使用JavaScript语言实现页面中表单的验证功能。
]
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>JavaScript--实例</title>
</head>
<body>
<h3 id="hid">JavaScript实例--表单事件</h3>
<form action="my.html" name="myform" method="post" onsubmit="return doSubmit()">
账号:<input type="text" name="uname"/> 8-16位的有效字符<br/><br/>
密码:<input type="password" name="upass"/> 6-18位<br/><br/>
邮箱:<input type="text" name="email"/> <br/><br/>
<input type="submit" value="提交"/>
</form>
<script type="text/javascript">
//表单提交事件处理
function doSubmit(){
//验证账号
var name = document.myform.uname.value;
if(name.match(/^\w{8,16}$/)==null){
alert("账号必须为8-16的有效字符!");
return false;
}
//验证密码
var pass = document.myform.upass.value;
if(pass.match(/^.{6,18}$/)==null){
alert("密码必须为6-18位!");
return false;
}
//验证邮箱
var email = document.myform.email.value;
if(email.match(/^\w+@\w+(\.\w+){1,2}$/)==null){
alert("请输入正确的Email地址!");
return false;
}
return true;
}
</script>
</body>
</html>
4.3 正则表达式在MySQL数据库中的使用–数据查询
MariaDB [mydb]> select * from stu;
+----+----------+-----+-----+----------+
| id | name | age | sex | classid |
+----+----------+-----+-----+----------+
| 1 | zhangsan | 22 | m | python03 |
| 2 | lisi | 25 | w | python04 |
| 3 | wangwu | 20 | m | python03 |
| 4 | zhaoliu | 19 | w | python04 |
| 5 | qq01 | 20 | m | python03 |
| 6 | qqmn | 21 | w | python04 |
| 7 | qq03 | 20 | m | python05 |
| 8 | uu01 | 21 | w | python04 |
| 9 | uu02 | 20 | m | python05 |
| 10 | aa | 29 | w | python03 |
| 11 | bb | 20 | m | python04 |
| 16 | abc | 25 | m | python05 |
+----+----------+-----+-----+----------+
12 rows in set (0.00 sec)
-- 使用正则查询姓名是使用任意两位小写字母构成的数据信息
MariaDB [mydb]> select * from stu where name regexp '^[a-z]{2}$';
+----+------+-----+-----+----------+
| id | name | age | sex | classid |
+----+------+-----+-----+----------+
| 10 | aa | 29 | w | python03 |
| 11 | bb | 20 | m | python04 |
+----+------+-----+-----+----------+
2 rows in set (0.00 sec)
--查询name的值为2~4位的小写字母
MariaDB [mydb]> select * from stu where name regexp '^[a-z]{2,4}$';
+----+------+-----+-----+----------+
| id | name | age | sex | classid |
+----+------+-----+-----+----------+
| 2 | lisi | 25 | w | python04 |
| 6 | qqmn | 21 | w | python04 |
| 10 | aa | 29 | w | python03 |
| 11 | bb | 20 | m | python04 |
| 16 | abc | 25 | m | python05 |
+----+------+-----+-----+----------+
5 rows in set (0.00 sec)
-- 查询name字段值是由两位字母加两位数字构成的数据信息
MariaDB [mydb]> select * from stu where name regexp '^[a-z]{2}[0-9]{2}$';
+----+------+-----+-----+----------+
| id | name | age | sex | classid |
+----+------+-----+-----+----------+
| 5 | qq01 | 20 | m | python03 |
| 7 | qq03 | 20 | m | python05 |
| 8 | uu01 | 21 | w | python04 |
| 9 | uu02 | 20 | m | python05 |
+----+------+-----+-----+----------+
4 rows in set (0.00 sec)
MariaDB [mydb]>
5. 网络爬虫概述
5.1 网络爬虫概述:
- 网络爬虫(Web Spider)又称网络蜘蛛、网络机器人,是一种按照一定的规则,自动地抓取万维网信息的程序或者脚本。
- 网络爬虫按照系统结构和实现技术,大致可分为一下集中类型:
- 通用网络爬虫:就是尽可能大的网络覆盖率,如 搜索引擎(百度、雅虎和谷歌等…)。
- 聚焦网络爬虫:有目标性,选择性地访问万维网来爬取信息。
- 增量式网络爬虫:只爬取新产生的或者已经更新的页面信息。特点:耗费少,难度大
- 深层网络爬虫:通过提交一些关键字才能获取的Web页面,如登录或注册后访问的页面。
- 注:实际工作中通常是几种爬虫技术结合实现。
5.2 应用场景:
- 爬虫技术在
科学研究
、Web安全
、产品研发
、舆情监控
等领域可以做很多事情。 - 在数据挖掘、机器学习、图像处理等科学研究领域,如果没有数据,则可以通过爬虫从网上抓取;
- 在Web安全方面,使用爬虫可以对网站是否存在某一漏洞进行批量验证、利用;
- 在产品研发方面,可以采集各个商城物品价格,为用户提供市场最低价;
- 在舆情监控方面,可以抓取、分析新浪微博的数据,从而识别出某用户是否为水军
5.3. 学习爬虫前的技术准备:
- (1). Python基础语言: 基础语法、运算符、数据类型、流程控制、函数、对象 模块、文件操作、多线程、网络编程 … 等
- (2). W3C标准: HTML、CSS、JavaScript、Xpath、JSON
- (3). HTTP标准: HTTP的请求过程、请求方式、状态码含义,头部信息以及Cookie状态管理
- (4). 数据库: SQLite、MySQL、MongoDB、Redis …
5.4 关于爬虫的合法性:
几乎每个网站都有一个名为robots.txt的文档,当然也有有些网站没有设定。对于没有设定robots.txt的网站可以通过网络爬虫获取没有口令加密的数据,也就是该网站所有页面的数据都可以爬取。如果网站有文件robots.txt文档,就要判断是否有禁止访客获取数据 如:https://www.taobao.com/robots.txt
6. 网络爬虫工作原理
6.1 网络爬虫使用的技术–数据抓取:
- 在爬虫实现上,除了scrapy框架之外,python有许多与此相关的库可供使用。其中,在数据抓取方面包括: urllib2(urllib3)、requests、mechanize、selenium、splinter;
- 其中,urllib2(urllib3)、requests、mechanize用来获取URL对应的原始响应内容;而selenium、splinter通过加载浏览器驱动,获取浏览器渲染之后的响应内容,模拟程度更高。
- 考虑效率、当然能使用urllib2(urllib3)、requests、mechanize等解决的尽量不用selenium、splinter,因为后者因需要加载浏览器而导致效率较低。
- 对于数据抓取,涉及的过程主要是模拟浏览器向服务器发送构造好的http请求,常见类型有:get/post。
6.2 网络爬虫使用的技术–数据解析:
- 在数据解析方面,相应的库包括:lxml、beautifulsoup4、re、pyquery。
- 对于数据解析,主要是从响应页面里提取所需的数据,常用方法有:xpath路径表达式、CSS选择器、正则表达式等。
- 其中,xpath路径表达式、CSS选择器主要用于提取结构化的数据。而正则表达式主要用于提取非结构化的数据。
7. 网络爬虫基础使用
1. urllib介绍:
-
在Python2版本中,有urllib和urlib2两个库可以用来实现request的发送。
-
而在Python3中,已经不存在urllib2这个库了,统一为urllib。
-
Python3 urllib库官方链接:
https://docs.python.org/3/library/urllib.html
- urllib中包括了四个模块,包括:
- urllib.request:可以用来发送request和获取request的结果
- urllib.error:包含了urllib.request产生的异常
- urllib.parse:用来解析和处理URL
- urllib.robotparse:用来解析页面的robots.txt文件
2. urllib.request:
- urllib.request 模块提供了最基本的构造 HTTP 请求方法,可以模拟浏览器的一个请求发起过程。同时它还带有处理authenticaton(授权验证),redirections(重定向),cookies(浏览器Cookies)以及其它内容。
- urllib.request.urlopen(url, data=None, [timeout, ]*, cafile=None, capath=None, cadefault=False, context=None)
- 参数:url地址
- data:可选附加参数,字节流编码格式(bytes() 可转换),请求方式会变为POST
- timeout (超时时间)单位为秒,若请求超出了设置时间还没有响应,则抛异常
- 返回HTTPResposne类型的对象:
- response.read() 就可以得到返回的网页内容,可使用decode(“utf-8”)解码字符串
- response.status 就可以得到返回结果的状态码,如200代表请求成功,404代表网页未找到
3. urllib.request.Request:
- 利用urlopen()方法可以实现最基本的请求发起,但这几个简单的参数并不足以构建一个完整的请求。
- 使用强大的Request类可以在请求中加入需要的headers等信息。
class urllib.request.Request(url, data=None, headers={}, origin_req_host=None,
unverifiable=False, method=None)
- 第一个参数是请求链接,这个是必传参数,其他的都是可选参数
- data 参数如果要传必须传 bytes (字节流)类型的,如果是一个字典,可以先用 urllib.parse.urlencode() 编码。
- headers 参数是一个字典,你可以在构造 Request 时通过 headers 参数传递,也可以通过调用 Request 对象的 add_header() 方法来添加请求头。
- origin_req_host 指的是请求方的 host 名称或者 IP 地址。
- unverifiable 指的是这个请求是否是无法验证的,默认是 False 。意思就是说用户没有足够权限来选择接收这个请求的结果
- method 是一个字符串,它用来指示请求使用的方法,比如 GET , POST , PUT 等等。
8. 网络爬虫实战
-
案例:爬取百度新闻首页的新闻标题信息
-
url地址:http://news.baidu.com/
-
具体实现步骤:
- 导入urlib库和re正则
- 使用urllib.request.Request()创建request请求对象
- 使用urllib.request.urlopen执行信息爬取,并返回Response对象
- 使用read()读取信息,使用decode()执行解码
- 使用re正则解析结果
- 遍历输出结果信息
-
具体代码如下:
import urllib.request
import re
url = "http://news.baidu.com/"
#伪装浏览器用户
headers = {'User-Agent':'User-Agent:Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0)'}
req = urllib.request.Request(url,headers=headers)
#执行请求获取响应信息
res = urllib.request.urlopen(req)
# 从响应对象中读取信息并解码
html = res.read().decode("utf-8")
#print(len(html))
#使用正则解析出新闻标题信息
pat = '<a href="(.*?)" .*? target="_blank">(.*?)</a>'
dlist = re.findall(pat,html)
# 遍历输出结果
for v in dlist:
print(v[1]+":"+v[0])
9. 网络爬虫中的异常处理
- 在网络爬虫运行时出现异常,若不处理则会因报错而终止运行,导致爬取数据中断,所以异常处理还是十分重要的。
urllib.error
可以接收有urllib.request
产生的异常。urllib.error
有两个类,URLError
和HTTPError
。URLError
内有一个属性:reason
返回错误的原因
# 测试URLError的异常处理
from urllib import request
from urllib import error
url = "http://www.wer3214e13wer3.com/"
req = request.Request(url)
try:
response = request.urlopen(req)
html = response.read().decode('utf-8')
print(len(html))
except error.URLError as e:
print(e.reason) #输出错误信息
print("ok")
- 报错信息,但程序继续执行:
[Errno 8] nodename nor servname provided, or not known
ok
HTTPError
内有三个属性:code
返回HTTP状态码,如404 ;reason
返回错误原因;headers
返回请求头
# 测试HTTPError的异常处理
from urllib import request
from urllib import error
url = "https://img-ads.csdn.net/2018/20180420184005werqwefsd9410.png"
req = request.Request(url)
try:
response = request.urlopen(req)
html = response.read().decode('utf-8')
print(len(html))
except error.HTTPError as e:
print(e.reason) #输出错误信息
print(e.code) #输出HTTP状态码
print("ok")
- 报的错误
Not Found
404
ok
URLError
是OSError
的一个子类,HTTPError
是URLError
的一个子类:- 注意:父类一定要在后面:
from urllib import request
from urllib import error
#url = "https://img-ads.csdn.net/2018/20180420184005werqwefsd9410.png"
url = "http://www.wer3214e13wer3.com/"
req = request.Request(url)
try:
response = request.urlopen(req)
html = response.read().decode('utf-8')
print(len(html))
except error.HTTPError as e:
print("HTTPError")
print(e.reason)
print(e.code)
except error.URLError as e:
print("URLError")
print(e.reason) # 输出错误信息
print("ok")
- 不什么错误都去处理:
from urllib import request
from urllib import error
url = "https://img-ads.csdn.net/2018/20180420184005werqwefsd9410.png"
#url = "http://www.wer3214e13wer3.com/"
req = request.Request(url)
try:
response = request.urlopen(req)
html = response.read().decode('utf-8')
print(len(html))
except Exception as e:
if hasattr(e,'reason'):
print(e.reason)
if hasattr(e,'code'):
print(e.code)
print("ok")
Not Found
404
ok
zhang
10. Urllib3和requests的使用
- Python3 默认提供了urllib库,可以爬取网页信息,但其中确实有不方便的地方,如:处理网页验证和Cookies,以及Hander头信息处理。
- 为了更加方便处理,有了更为强大的库
urllib3
和requests
, 本节会分别介绍一下,以后我们着重使用requests
。 - urllib3网址:https://pypi.org/project/urllib3/
- requests网址:http://www.python-requests.org/en/master/
1. urllib3库的使用:
- 安装:通过使用pip命令来安装urllib3
pip install urllib3
- 简单使用:
import urllib3
import re
# 实例化产生请求对象
http = urllib3.PoolManager()
# get请求指定网址
url = "http://www.baidu.com"
res = http.request("GET",url)
# 获取HTTP状态码
print("status:%d" % res.status)
# 获取响应内容
data = res.data.decode("utf-8")
# 正则解析并输出
print(re.findall("<title>(.*?)</title>",data))
- 其他设置: 增加了超时时间,请求参数等设置
import urllib3
import re
url = "http://www.baidu.com"
http = urllib3.PoolManager(timeout = 4.0) #设置超时时间
res = http.request(
"GET",
url,
#headers={
# 'User-Agent':'Mozilla/5.0(WindowsNT6.1;rv:2.0.1)Gecko/20100101Firefox/4.0.1',
#},
fields={'id':100,'name':'lisi'}, #请求参数信息
)
print("status:%d" % res.status)
data = res.data.decode("utf-8")
print(re.findall("<title>(.*?)</title>",data))
2. requests库的使用:
- 安装:通过使用pip命令来安装requests
pip install requests
- 简单使用:
import requests
import re
url = "http://www.baidu.com"
# 抓取信息
res = requests.get(url)
#获取HTTP状态码
print("status:%d" % res.status_code)
# 获取响应内容
data = res.content.decode("utf-8")
#解析出结果
print(re.findall("<title>(.*?)</title>",data))
- 关于请求中的参数下节课再讲
11. GET请求爬取数据实战
- 使用urllib的GET获取58同城中关于python的招聘信息
from urllib import request
from urllib import error
import re
url = "http://bj.58.com/job/?key=python&final=1&jump=1"
req = request.Request(url)
try:
response = request.urlopen(req)
html = response.read().decode('utf-8')
pat = '<span class="address" >(.*?)</span> \| <span class="name">(.*?)</span>'
dlist = re.findall(pat,html)
#print(len(dlist))
for v in dlist:
print(v[0]+" | "+v[1])
except error.URLError as e:
print(e.reason) #输出错误信息
print("ok")
- 使用requests的GET获取58同城中关于python的招聘信息
import requests
import re
data = {
'key':'python',
'final':1,
'jump':1,
}
url = "http://bj.58.com/job/"
res = requests.get(url,params=data)
html = res.content.decode('utf-8')
pat = '<span class="address" >(.*?)</span> \| <span class="name">(.*?)</span>'
dlist = re.findall(pat,html)
print(len(dlist))
for v in dlist:
print(v[0]+" | "+v[1])
12. POST请求爬取数据实战
import json
json.loads(json_str) # json字符串转换成字典
json.dumps(dict) # 字典转换成json字符串
- 使用urllib发送POST数据,并抓取百度翻译信息
from urllib import request,parse
import json
url = 'http://fanyi.baidu.com/sug'
# 定义请求参数
data = {
'kw' : 'python'
}
data = parse.urlencode(data) #编码转换
# 封装headers头信息
headers = {
'Content-Length' : len(data)
}
# 发送请求,抓取信息
req = request.Request(url=url,data=bytes(data,encoding='utf-8'),headers=headers)
res = request.urlopen(req)
# 解析结果并输出
str_json = res.read().decode('utf-8') # json
myjson = json.loads(str_json) # 把json转字典
info = myjson['data'][0]['v']
print(info)
- 使用requests发送POST数据,并抓取百度翻译信息
import requests
import json
url = 'http://fanyi.baidu.com/sug'
# 定义请求参数
data = {
'kw' : 'python'
}
# 发送请求,抓取信息
res = requests.post(url,data=data)
# 解析结果并输出
str_json = res.content.decode('utf-8') # 获取响应的json字串
myjson = json.loads(str_json) # 把json转字典
info = myjson['data'][0]['v']
print(info)
- 使用requests 实现翻译信息抓取
import requests
import json
def fanyi(keyword):
url = 'http://fanyi.baidu.com/sug'
# 定义请求参数
data = {
'kw' : keyword
}
# 发送请求,抓取信息
res = requests.post(url,data=data)
# 解析结果并输出
str_json = res.content.decode('utf-8') # 获取响应的json字串
myjson = json.loads(str_json) # 把json转字典
info = myjson['data'][0]['v']
print(info)
if __name__ == '__main__':
while True:
keyword = input('输入翻译的单词:')
if keyword == 'q':
break
fanyi(keyword)
- urllib模式:
from urllib import request,parse
import json
def fanyi(keyword):
base_url = 'http://fanyi.baidu.com/sug'
# 构建请求对象
data = {
'kw' : keyword
}
data = parse.urlencode(data)
headers = {
'Content-Length':len(data)
}
req = request.Request(url=base_url,data=bytes(data,encoding='utf-8'),headers=headers)
res = request.urlopen(req)
str_json = res.read().decode('utf-8') # 获取响应的json字串
myjson = json.loads(str_json) # 把json转字典
info = myjson['data'][0]['v']
print(info)
if __name__ == '__main__':
while True:
keyword = input('输入翻译的单词:')
if keyword == 'q':
break
fanyi(keyword)
13. 网络爬虫案例实战1
- 本次案例是通过登录人人网,抓取登录后用户中心的信息
1. 模拟人人登录请求,执行登录验证操作
from urllib import request,parse
login_url = 'http://www.renren.com/ajaxLogin/login?1=1&uniqueTimestamp=2018321648829'
data = {
'email':'1352*****6',
'icode':'',
'origURL':'http://www.renren.com/home',
'domain':'renren.com',
'key_id':'1',
'captcha_type':'web_login',
'password':'478b7c2dca554eeabed3b7374703bff4a6a22e78b8a9fcfb090e3a7fb792992b',
'rkey':'e954ec64a7ecf4e33bdf81bb1abad158',
'f':'http%3A%2F%2Fwww.renren.com%2F965541786',
}
data = parse.urlencode(data)
headers = {
'Content-Length' : len(data)
}
req = request.Request(url=login_url,data=bytes(data,encoding='utf-8'),headers=headers,)
response = request.urlopen(req)
print(response.read().decode('utf-8'))
2. 抓取登录成功后的用户home页信息
from urllib import request
import re,gzip
base_url = 'http://www.renren.com/965541786'
headers = {
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
'Accept-Encoding': 'gzip, deflate',
'Accept-Language': 'zh-CN,zh;q=0.9',
'Cache-Control': 'max-age=0',
'Connection': 'keep-alive',
'Cookie': 'anonymid=jgdcqcjgqy4yxt; depovince=BJ; _r01_=1; JSESSIONID=abc7HUv9M_HsB7WkgK2lw; ick_login=b954cf62-bbe5-480d-b679-e1e3ce584896; SL_GWPT_Show_Hide_tmp=1; SL_wptGlobTipTmp=1; ick=b4337770-b7ce-4a70-b9d0-cd63c7fc7bb5; XNESSESSIONID=738f4bde312f; jebe_key=f950add1-40e8-4009-a157-bfc3d89f7350%7C24a48cb369f8637c5ee2c4a23eb5b93f%7C1524555370510%7C1%7C1524555375485; first_login_flag=1; ln_uact=13520319616; ln_hurl=http://head.xiaonei.com/photos/0/0/men_main.gif; wp_fold=0; jebecookies=ef7f7372-0e70-45db-aaae-c415d4611918|||||; _de=8C2F648D7158ED727318288C8F3F21C5; p=f1ea4b6984cefb7d88164a67816c91fe6; t=401516286d37bde6735180d25f68f2fe6; societyguester=401516286d37bde6735180d25f68f2fe6; id=965541786; xnsid=928c27b; ver=7.0; loginfrom=null',
'Host': 'www.renren.com',
'Referer': 'http://www.renren.com/SysHome.do',
'Upgrade-Insecure-Requests': '1',
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36',
}
req = request.Request(url=base_url,headers=headers)
res = request.urlopen(req)
#html = res.read().decode("utf-8") # 网页响应时开启了gzip压缩,需要解压
#报错:UnicodeDecodeError: 'utf-8' codec can't decode byte 0x8b in position 1: invalid start byte
# 获取将请求头中的Accept-Encoding的gzip删除也可以
# 对gzip压缩的响应,我们解压后转码
html = gzip.decompress(res.read()).decode("utf-8")
#print(html)
print(re.findall("<title>(.*?)</title>",html))
3. 使用cookiejar将上面两个合并到一起执行
from urllib import request,parse
import re,gzip,time
# cookie管理模块,
from http import cookiejar
# 返回存储cookie对象
cookie = cookiejar.CookieJar()
# 返回一个cookie管理器
cookie_handler = request.HTTPCookieProcessor(cookie)
# 请求管理器
opener = request.build_opener(cookie_handler)
def doLogin():
login_url = 'http://www.renren.com/ajaxLogin/login?1=1&uniqueTimestamp=2018321648829'
data = {
'email':'1352*****16',
'icode':'',
'origURL':'http://www.renren.com/home',
'domain':'renren.com',
'key_id':'1',
'captcha_type':'web_login',
'password':'478b7c2dca554eeabed3b7374703bff4a6a22e78b8a9fcfb090e3a7fb792992b',
'rkey':'e954ec64a7ecf4e33bdf81bb1abad158',
'f':'http%3A%2F%2Fwww.renren.com%2F965541786',
}
data = parse.urlencode(data)
headers = {
'Content-Length' : len(data)
}
req = request.Request(url=login_url,data=bytes(data,encoding='utf-8'),headers=headers,)
response = opener.open(req)
def myHome():
home_url = 'http://www.renren.com/965541786'
res = opener.open(home_url)
html = res.read().decode("utf-8")
#print(html)
print(re.findall("<title>(.*?)</title>",html))
if __name__ == '__main__':
# 登陆
print("正在登录中...")
doLogin()
time.sleep(3)
# 访问个人首页
myHome()
4. 使用requests重写第三步的代码,实现人人网登录并抓取登录后信息
import requests
import re,time
s = requests.Session()
def doLogin():
login_url = 'http://www.renren.com/ajaxLogin/login?1=1&uniqueTimestamp=2018321648829'
data = {
'email':'1352*****6',
'icode':'',
'origURL':'http://www.renren.com/home',
'domain':'renren.com',
'key_id':'1',
'captcha_type':'web_login',
'password':'478b7c2dca554eeabed3b7374703bff4a6a22e78b8a9fcfb090e3a7fb792992b',
'rkey':'e954ec64a7ecf4e33bdf81bb1abad158',
'f':'http%3A%2F%2Fwww.renren.com%2F965541786',
}
s.post(login_url,data=data)
def myHome():
home_url = 'http://www.renren.com/965541786'
res = s.get(home_url)
html = res.content.decode("utf-8")
#print(html)
print(re.findall("<title>(.*?)</title>",html))
if __name__ == '__main__':
# 登陆
print("正在登录中...")
doLogin()
time.sleep(3)
# 访问个人首页
myHome()
13. 网络爬虫案例实战2
1. 爬取猫眼电影中榜单栏目中TOP100榜的所有电影信息,并将信息写入文件
- 目标:使用urllib分页爬取猫眼电影中榜单栏目中TOP100榜的所有电影信息,并将信息写入文件
- URL地址:http://maoyan.com/board/4 其中参数offset表示其实条数
- 获取信息:{排名,图片,标题,主演,放映时间,评分}
from urllib import request,error
import re,time,json
def getPage(url):
'''爬取指定url页面信息'''
try:
#定义请求头信息
headers = {
'User-Agent':'User-Agent:Mozilla/5.0(WindowsNT6.1;rv:2.0.1)Gecko/20100101Firefox/4.0.1'
}
# 封装请求对象
req = request.Request(url,headers=headers)
# 执行爬取
res = request.urlopen(req)
#判断响应状态,并响应爬取内容
if res.code == 200:
return res.read().decode("utf-8")
else:
return None
except error.URLError:
return None
def parsePage(html):
'''解析爬取网页中的内容,并返回字段结果'''
#定义解析正则表达式
pat = '<i class="board-index board-index-[0-9]+">([0-9]+)</i>.*?<img data-src="(.*?)" alt="(.*?)" class="board-img" />.*?<p class="star">(.*?)</p>.*?<p class="releasetime">(.*?)</p>.*?<i class="integer">([0-9\.]+)</i><i class="fraction">([0-9]+)</i>'
#执行解析
items = re.findall(pat,html,re.S)
#遍历封装数据并返回
for item in items:
yield {
'index':item[0],
'image':item[1],
'title':item[2],
'actor':item[3].strip()[3:],
'time':item[4].strip()[5:],
'score':item[5]+item[6],
}
def writeFile(content):
'''执行文件追加写操作'''
#print(content)
with open("./result.txt",'a',encoding='utf-8') as f:
f.write(json.dumps(content,ensure_ascii=False) + "\n")
#json.dumps 序列化时对中文默认使用的ascii编码.想输出真正的中文需要指定ensure_ascii=False
def main(offset):
''' 主程序函数,负责调度执行爬虫处理 '''
url = 'http://maoyan.com/board/4?offset=' + str(offset)
#print(url)
html = getPage(url)
#判断是否爬取到数据,并调用解析函数
if html:
for item in parsePage(html):
writeFile(item)
# 判断当前执行是否为主程序运行,并遍历调用主函数爬取数据
if __name__ == '__main__':
for i in range(10):
main(offset=i*10)
time.sleep(1)
- 目标:使用requests分页爬取猫眼电影中榜单栏目中TOP100榜的所有电影信息,并将信息写入文件
- URL地址:http://maoyan.com/board/4 其中参数offset表示其实条数
- 获取信息:{排名,图片,标题,主演,放映时间,评分}
from requests.exceptions import RequestException
import requests
import re,time,json
def getPage(url):
'''爬取指定url页面信息'''
try:
#定义请求头信息
headers = {
'User-Agent':'User-Agent:Mozilla/5.0(WindowsNT6.1;rv:2.0.1)Gecko/20100101Firefox/4.0.1'
}
# 执行爬取
res = requests.get(url,headers=headers)
#判断响应状态,并响应爬取内容
if res.status_code == 200:
return res.text
else:
return None
except RequestException:
return None
def parsePage(html):
'''解析爬取网页中的内容,并返回字段结果'''
#定义解析正则表达式
pat = '<i class="board-index board-index-[0-9]+">([0-9]+)</i>.*?<img data-src="(.*?)" alt="(.*?)" class="board-img" />.*?<p class="star">(.*?)</p>.*?<p class="releasetime">(.*?)</p>.*?<i class="integer">([0-9\.]+)</i><i class="fraction">([0-9]+)</i>'
#执行解析
items = re.findall(pat,html,re.S)
#遍历封装数据并返回
for item in items:
yield {
'index':item[0],
'image':item[1],
'title':item[2],
'actor':item[3].strip()[3:],
'time':item[4].strip()[5:],
'score':item[5]+item[6],
}
def writeFile(content):
'''执行文件追加写操作'''
#print(content)
with open("./result.txt",'a',encoding='utf-8') as f:
f.write(json.dumps(content,ensure_ascii=False) + "\n")
#json.dumps 序列化时对中文默认使用的ascii编码.想输出真正的中文需要指定ensure_ascii=False
def main(offset):
''' 主程序函数,负责调度执行爬虫处理 '''
url = 'http://maoyan.com/board/4?offset=' + str(offset)
#print(url)
html = getPage(url)
#判断是否爬取到数据,并调用解析函数
if html:
for item in parsePage(html):
writeFile(item)
# 判断当前执行是否为主程序运行,并遍历调用主函数爬取数据
if __name__ == '__main__':
for i in range(10):
main(offset=i*10)
time.sleep(1)
八、Python网络爬虫基础(下)
- 在上一周我们实现了一个基本的网络爬虫,但是提取页面信息时使用的是正则表达式,这还是比较烦琐,出错率比较高。
- 我们爬取的信息大多都是网页信息,网页HTML节点(标签)中定义了大量的id和class属性,而且节点之间还有层级关系。
- 针对于上述这种格式的解析,给大家介绍几种解析库:
14. 解析库的使用
14-1. 解析库的使用–XPath
- XPath(XML Path Language)是一门在XML文档中查找信息的语言。
- XPath 可用来在XML文档中对元素和属性进行遍历。
- XPath 是
W3C XSLT
标准的主要元素,并且XQuery
和XPointer
都构建于 XPath 表达之上。 - 官方网址:
http://lxml.de
官方文档:http://lxml.de/api/index.html
- 注:XQuery 是用于 XML 数据查询的语言(类似SQL查询数据库中的数据)
- 注:XPointer 由统一资源定位地址(URL)中#号之后的描述组成,类似于HTML中的锚点链接
- python中如何安装使用XPath:
- ①: 安装 lxml 库。
- ②: from lxml import etree
- ③: Selector = etree.HTML(网页源代码)
- ④: Selector.xpath(一段神奇的符号)
1. 准备工作:
- 要使用XPath首先要先安装lxml库:
pip install lxml
2. XPath选取节点规则
表达式 | 描述 |
---|---|
nodename | 选取此节点的所有子节点。 |
/ | 从当前节点选取直接子节点 |
// | 从匹配选择的当前节点选择所有子孙节点,而不考虑它们的位置 |
. | 选取当前节点。 |
… | 选取当前节点的父节点。 |
@ | 选取属性。 |
- XPath 运算符
运算符 | 描述 | 实例 | 返回值 |
---|---|---|---|
¦ | 计算两个节点集 | //book ¦ //cd | 返回所有拥有 book 和 cd 元素的节点集 |
+ | 加法 | 6 + 4 | 10 |
- | 减法 | 6 - 4 | 2 |
* | 乘法 | 6 * 4 | 24 |
div | 除法 | 8 div 4 | 2 |
= | 等于 | price=9.80 | 如果 price 是 9.80,则返回 true。、\n 如果 price 是 9.90,则返回 false。 |
!= | 不等于 | price!=9.80 | 如果 price 是 9.90,则返回 true。\n 如果 price 是 9.80,则返回 false。 |
< | 小于 | price<9.80 | 如果 price 是 9.00,则返回 true。\n 如果 price 是 9.90,则返回 false。 |
<= | 小于或等于 | price<=9.80 | 如果 price 是 9.00,则返回 true。\n 如果 price 是 9.90,则返回 false。 |
> | 大于 | price>9.80 | 如果 price 是 9.90,则返回 true。\n如果 price 是 9.80,则返回 false。 |
>= | 大于或等于 | price>=9.80 | 如果 price 是 9.90,则返回 true。\n如果 price 是 9.70,则返回 false。 |
or | 或 | price=9.80 or price=9.70 | 如果 price 是 9.80,则返回 true。\n如果 price 是 9.50,则返回 false。 |
and | 与 | price>9.00 and price<9.90 | 如果 price 是 9.80,则返回 true。\n如果 price 是 8.50,则返回 false。 |
mod | 计算除法的余数 | 5 mod 2 | 1 |
3. 解析案例:
- 首先创建一个html文件:my.html 用于测试XPath的解析效果
<!DOCTYPE html>
<html>
<head>
<title>我的网页</title>
</head>
<body>
<h3 id="hid">我的常用链接</h3>
<ul>
<li class="item-0"><a href="http://www.baidu.com">百度</a></li>
<li class="item-1 shop"><a href="http://www.jd.com">京东</a></li>
<li class="item-2"><a href="http://www.sohu.com">搜狐</a></li>
<li class="item-3"><a href="http://www.sina.com">新浪</a></li>
<li class="item-4 shop"><a href="http://www.taobao.com">淘宝</a></li>
</ul>
</body>
</html>
- 使用XPath解析说明
# 导入模块
from lxml import etree
# 读取html文件信息(在真实代码中是爬取的网页信息)
f = open("./my.html",'r',encoding="utf-8")
content = f.read()
f.close()
# 解析HTML文档,返回根节点对象
html = etree.HTML(content)
#print(html) # <Element html at 0x103534c88>
# 获取网页中所有标签并遍历输出标签名
result = html.xpath("//*")
for t in result:
print(t.tag,end=" ")
#[html head title body h3 ul li a li a ... ... td]
print()
# 获取节点
result = html.xpath("//li") # 获取所有li节点
result = html.xpath("//li/a") # 获取所有li节点下的所有直接a子节点
result = html.xpath("//ul//a") # 效果同上(ul下所有子孙节点)
result = html.xpath("//a/..") #获取所有a节点的父节点
print(result)
# 获取属性和文本内容
result = html.xpath("//li/a/@href") #获取所有li下所有直接子a节点的href属性值
result = html.xpath("//li/a/text()") #获取所有li下所有直接子a节点内的文本内容
print(result) #['百度', '京东', '搜狐', '新浪', '淘宝']
result = html.xpath("//li/a[@class]/text()") #获取所有li下所有直接含有class属性子a节点内的文本内容
print(result) #['百度', '搜狐', '新浪']
#获取所有li下所有直接含有class属性值为aa的子a节点内的文本内容
result = html.xpath("//li/a[@class='aa']/text()")
print(result) #['搜狐', '新浪']
#获取class属性值中含有shop的li节点下所有直接a子节点内的文本内容
result = html.xpath("//li[contains(@class,'shop')]/a/text()")
print(result) #['搜狐', '新浪']
# 按序选择
result = html.xpath("//li[1]/a/text()") # 获取每组li中的第一个li节点里面的a的文本
result = html.xpath("//li[last()]/a/text()") # 获取每组li中最后一个li节点里面的a的文本
result = html.xpath("//li[position()<3]/a/text()") # 获取每组li中前两个li节点里面的a的文本
result = html.xpath("//li[last()-2]/a/text()") # 获取每组li中倒数第三个li节点里面的a的文本
print(result)
print("--"*30)
# 节点轴选择
result = html.xpath("//li[1]/ancestor::*") # 获取li的所有祖先节点
result = html.xpath("//li[1]/ancestor::ul") # 获取li的所有祖先中的ul节点
result = html.xpath("//li[1]/a/attribute::*") # 获取li中a节点的所有属性值
result = html.xpath("//li/child::a[@href='http://www.sohu.com']") #获取li子节点中属性href值的a节点
result = html.xpath("//body/descendant::a") # 获取body中的所有子孙节点a
print(result)
result = html.xpath("//li[3]") #获取li中的第三个节点
result = html.xpath("//li[3]/following::li") #获取第三个li节点之后所有li节点
result = html.xpath("//li[3]/following-sibling::*") #获取第三个li节点之后所有同级li节点
for v in result:
print(v.find("a").text)
- 解析案例
# 导入模块
from lxml import etree
# 读取html文件信息(在真实代码中是爬取的网页信息)
f = open("./my.html",'r')
content = f.read()
f.close()
# 解析HTML文档,返回根节点对象
html = etree.HTML(content)
# 1. 获取id属性为hid的h3节点中的文本内容
print(html.xpath("//h3[@id='hid']/text()")) #['我的常用链接']
# 2. 获取li中所有超级链接a的信息
result = html.xpath("//li/a")
for t in result:
# 通过xapth()二次解析结果
#print(t.xpath("text()")[0], ':', t.xpath("@href")[0])
# 效果同上,使用节点对象属性方法解析
print(t.text, ':', t.get("href"))
'''
#结果:
百度 : http://www.baidu.com
京东 : http://www.jd.com
搜狐 : http://www.sohu.com
新浪 : http://www.sina.com
淘宝 : http://www.taobao.com
'''
'''
HTML元素的属性:
tag:元素标签名
text:标签中间的文本
HTML元素的方法:
find() 查找一个匹配的元素
findall() 查找所有匹配的元素
get(key, default=None) 获取指定属性值
items()获取元素属性,作为序列返回
keys()获取属性名称列表
value是()将元素属性值作为字符串序列
'''
14-2. 解析库的使用–Beautiful Soup
- BeautifulSoup是Python的一个HTML或XML解析库,最主要的功能就是从网页爬取我们需要的数据。
- BeautifulSoup将html解析为对象进行处理,全部页面转变为字典或者数组,相对于正则表达式的方式,可以大大简化处理过程。
- Beautiful Soup3 目前已经停止开发,我们推荐在现在的项目中使用Beautiful Soup4,
1. BeautifulSoup 安装与使用:
- Beautiful Soup是一个依赖于lxml的解析库,所以在安装之前要先确保lxml库已安装:
pip install lxml
- 安装 BeautifulSoup 解析器:
pip install beautifulsoup4
-
官方文档:https://www.crummy.com/software/BeautifulSoup/bs4/doc/
-
中文文档:https://www.crummy.com/software/BeautifulSoup/bs4/doc.zh/
-
PyPI: https://pypi.python.org/pypi/beautifulsoup4
-
主要的解析器,以及它们的优缺点:
解析器 | 使用方法 | 优势 | 劣势 |
---|---|---|---|
Python标准库 | BeautifulSoup(markup, “html.parser”) | Python的内置标准库,执行速度适中,文档容错能力强 | Python 2.7.3 or 3.2.2前的版本中文档容错能力差 |
lxml HTML 解析器 | BeautifulSoup(markup, “lxml”) | 速度快 文档容错能力强 | 需要安装C语言库 |
lxml XML 解析器 | BeautifulSoup(markup, [“lxml-xml”]) BeautifulSoup(markup, “xml”) | 速度快 唯一支持XML的解析器 | 需要安装C语言库 |
html5lib | BeautifulSoup(markup, “html5lib”) | 最好的容错性,以浏览器的方式解析文档,生成HTML5格式的文档 | 速度慢、不依赖外部扩展 |
- lxml解析器有解析html和xml的功能,而且速度快,容错能力强,故推荐使用。
- 快速使用案例:
# 导入模块
from bs4 import BeautifulSoup
# 读取html文件信息(在真实代码中是爬取的网页信息)
f = open("./my.html",'r',encoding="utf-8")
content = f.read()
f.close()
# 创建解析器
soup = BeautifulSoup(content,"lxml")
# 输出网页内容:注:此内容已被缩进格式化(自动更正格式),其实这个是在上一步实例化时就已完成
print(soup.prettify())
#输出网页中title标签中的内容
print(soup.title.string)
2. 节点选择器:
- 直接调用节点的名称就可以选择节点元素,再调用string属性就可以得到节点内的文本了,这种方式速度非常快。
- ① 选择元素:
...
print(soup.title) #<title>我的网页</title>
print(type(soup.title)) #<class 'bs4.element.Tag'>
print(soup.head) #获取整个head元素,及内部元素
print(soup.li) #获取第一个li元素(后面其他li不会获取)
# <li class="item-0"><a class="bb" href="http://www.baidu.com">百度</a></li>
- ② 提取信息:
...
print(soup.a) #获取第一个a元素标签: <a class="bb" href="http://www.baidu.com">百度</a>
print(soup.a.name) #获取标签名: a
print(soup.a.attrs) #获取所有属性:{'class': ['bb'], 'href': 'http://www.baidu.com'}
print(soup.a.attrs['href']) #获取其中一个属性:http://www.baidu.com
print(soup.a.string) # 获取元素标签中间的文本内容:百度
- ③ 嵌套选择:
print(soup.li.a) #获取网页中第一个li中的第一个a元素节点
#输出 <a class="bb" href="http://www.baidu.com">百度</a>
print(type(soup.body.h3)) ##获取body中的第一个h3元素的类型:<class 'bs4.element.Tag'>
print(soup.body.h3.string) #获取body中的第一个h3中的文本内容: 我的常用链接
- ④ 关联选择:
- 我们在做选择的时候,难免有时候不能够一步就选到想要的节点元素。
- 需要先选中某一个节点元素,然后再基于这些继续向下选择(子,父,兄弟)。
#....
# 子或子孙节点
# 以下获取的节点列表都可以使用for...in遍历
print(soup.ul.contents) #获取ul下面的所有直接子节点,返回列表
print(soup.ul.children) #获取ul下面的所有直接子节点,返回一个:<list_iterator object at 0x110346a20>
print(soup.ul.descendants) # 获取ul下的所有子孙节点。
for v in soup.ul.descendants:
print("a:",v)
# 父祖节点
print(soup.a.parent.name) #通过parent属性获取a的父节点 li
# print(list(soup.a.parents)) # 获取所有祖先节点
#兄弟节点
print(soup.li.next_siblings) #获取指定li节点的所有后面的兄弟节点
print(soup.li.previous_siblings)#获取指定li节点的所有前面的兄弟节点
#for v in soup.li.next_siblings:
# print(v)
# 获取信息
print(soup.a.string) #获取a节点中的文本
print(soup.a.attrs['href']) # 或a节点的href属性值
3. 方法选择器:
- ① find_all() – 传入属性或文本,返回所有符合条件的元素 格式:
find_all(name,attrs,recursive,text, **kwargs )
# 导入模块
from bs4 import BeautifulSoup
import re
# 读取html文件信息(在真实代码中是爬取的网页信息)
f = open("./my.html",'r')
content = f.read()
f.close()
# 创建解析器
soup = BeautifulSoup(content,"lxml")
# 通过name指定li值,获取所有li元素节点,返回列表
lilist = soup.find_all(name="li")
# 通过attrs指定属性来获取所有元素节点
lilist = soup.find_all(attrs={"class":"aa"})
lilist = soup.find_all(class_="aa") #同上(class属性中包含就可以了)
lilist = soup.find_all(class_="shop") #class属性值中包含shop的所有节点
lilist = soup.find_all(id="hid") #<h3 id="hid">我的常用链接</h3>
# 通过文本内容获取
lilist = soup.find_all(text='百度') # 百度
lilist = soup.find_all(text=re.compile('张')) # 张翠山 张无忌
for i in lilist:
print(i)
- ② find() – 传入属性或文本,返回所有符合条件的第一个元素
# 获取一个li元素节点
lilist = soup.find(name="li")
# 通过attrs指定属性来获取一个元素节点
lilist = soup.find(attrs={"class":"aa"})
4. CSS选择器:
# 导入模块
from bs4 import BeautifulSoup
import re
# 读取html文件信息(在真实代码中是爬取的网页信息)
f = open("./my.html",'r')
content = f.read()
f.close()
# 创建解析器
soup = BeautifulSoup(content,"lxml")
print(soup.select("ul li a")) #获取ul里面li下面的a元素节点
print(soup.select("#hid")) #获取id属性值为hid的元素节点
print(soup.select("li.shop a")) #获取class属性为shop的li元素里面所有的a元素节点
# 套用选择解析器
blist = soup.select("ul li")
for li in blist:
a = li.select("a")[0] #获取每个li里面的a元素节点
print(a)
print(a['href']) #获取属性href的值
# print(a.attrs['href']) #等价 同上 获取属性值
print(a.get_text()) #等价 print(a.string) 获取元素节点的文本内容
14-3. 解析库的使用–pyquery
① PyQuery介绍与安装
-
PyQuery库也是一个非常强大又灵活的网页解析库,如果你有前端开发经验的,都应该接触过jQuery,那么PyQuery就是你非常绝佳的选择。
-
PyQuery 是 Python 仿照 jQuery 的严格实现。
-
语法与 jQuery 几乎完全相同,所以不用再去费心去记一些奇怪的方法了。
-
官网地址:http://pyquery.readthedocs.io/en/latest/
-
jQuery参考文档: http://jquery.cuishifeng.cn/
-
PyQuery的安装
pip install pyquery
- URL初始化:
# URL初始化
from pyquery import PyQuery as pq
doc = pq(url="http://www.baidu.com",encoding="utf-8")
print(doc('title'))
# 文件初始化
from pyquery import PyQuery as pq
doc = pq(filename='my.html',encoding="utf-8")
print(doc('title'))
'''
# 推荐使用requests爬取信息
from pyquery import PyQuery as pq
import requests
res = requests.get("http://www.baidu.com")
res.encoding = "utf-8" #因为原编码为ISO-8859-1
#print(res.text)
doc = pq(res.text)
print(doc("title"))
② PyQuery的使用
# 读取my.html的文件内容,并使用pyquery来查找节点
from pyquery import PyQuery as pq
doc = pq(filename='my.html',encoding="utf-8")
print(doc('title')) #通过html标签名获取元素节点
print(doc('#hid')) #获取id属性值为hid的元素节点
print(doc('.bb')) #获取class属性值为bb的元素节点
print(doc('title,h3')) #选择符组的使用
print(doc("ul li.shop a")) #关联选择符的使用
print(doc("a")) #获取所有a
print(doc("a:first")) #获取第一个a
print(doc("a:last")) #获取最后一个a
print(doc("a:lt(2)")) #获取前连个a
print(doc("a:eq(2)")) #获取索引位置2的a(第三个)
print(doc('a[href="http://www.sina.com"]')) #获取指定属性值的节点
print("="*60)
# 节点的二次筛选:
lilist = doc("ul li") #获取ul中所有的li
print(type(lilist)) #<class 'pyquery.pyquery.PyQuery'>
print(lilist.find("a.bb")) #在结果的基础上再次查找
print(lilist.children("a.bb")) #在结果的基础上再次查找
print(doc("a.bb").parent()) #获取指定元素的直接父节点
#print(doc("a.bb").parents()) #获取指定元素的所有父节点
print(doc("a.bb").parent().siblings()) #获取兄弟节点
print("="*60)
# 遍历:
alist = doc("a")
for a in alist.items():
print(a.attr.href)
#print(a.attr('href')) #同上
print(a.text()) #获取内容
print(a.html())
#节点操作具体参考手册
15. 豆瓣电影Top250信息爬取实战
- 通过本案例[豆瓣电影Top250信息爬取]锻炼除正则表达式之外三种信息解析方式:Xpath、BeautifulSoup和PyQuery。
- 爬取url地址:https://movie.douban.com/top250
1. 分析:
-
- 分析url地址:https://movie.douban.com/top250 每页25条数据,共计10页
第一页:https://movie.douban.com/top250?start=0
第二页:https://movie.douban.com/top250?start=25
第三页:https://movie.douban.com/top250?start=50
...
结果:
for i in range(10):
url = "https://movie.douban.com/top250?start="+str(i*25)
-
- 分析网页源代码内容:每部电影信息都是放在
<div class="item">...</div>
中
- 分析网页源代码内容:每部电影信息都是放在
2. 具体实现代码如下:
from requests.exceptions import RequestException
from lxml import etree
from bs4 import BeautifulSoup
from pyquery import PyQuery as pq
import requests
import re,time,json
def getPage(url):
'''爬取指定url页面信息'''
try:
#定义请求头信息
headers = {
'User-Agent':'User-Agent:Mozilla/5.0(WindowsNT6.1;rv:2.0.1)Gecko/20100101Firefox/4.0.1'
}
# 执行爬取
res = requests.get(url,headers=headers)
#判断响应状态,并响应爬取内容
if res.status_code == 200:
return res.text
else:
return None
except RequestException:
return None
def parsePage(content):
'''解析爬取网页中的内容,并返回字段结果'''
print(content)
# =========使用pyquery解析==================
# 解析HTML文档
doc = pq(content)
#获取网页中所有标签并遍历输出标签名
items = doc("div.item")
#遍历封装数据并返回
for item in items.items():
yield {
'index':item.find("div.pic em").text(),
'image':item.find("div.pic img").attr('src'),
'title':item.find("div.hd span.title").text(),
'actor':item.find("div.bd p:eq(0)").text(),
'score':item.find("div.bd div.star span.rating_num").text(),
}
'''
# =======使用Beautiful Soup解析====================
# 解析HTML文档
soup = BeautifulSoup(content,"lxml")
#获取网页中所有标签并遍历输出标签名
items = soup.find_all(name="div",attrs={"class":"item"})
print(items)
#遍历封装数据并返回
for item in items:
yield {
'index':item.em.string,
'image':item.find(name="img",attrs={'width':'100'}).attrs['src'],
'title':item.find(name="span",attrs={'class':'title'}).string,
'actor':item.select("div.bd p")[0].get_text(), #内有标签使用string获取不到
'score':item.select("div.star span")[1].string,
}
'''
'''
# =======使用xpath解析====================
# 解析HTML文档,返回根节点对象
html = etree.HTML(content)
#获取网页中所有标签并遍历输出标签名
items = html.xpath('//div[@class="item"]')
#遍历封装数据并返回
for item in items:
yield {
'index':item.xpath('.//div/em[@class=""]/text()')[0],
'image':item.xpath('.//img[@width="100"]/@src')[0],
'title':item.xpath('.//span[@class="title"]/text()')[0],
'actor':item.xpath('.//p[@class=""]/text()')[0],
'score':item.xpath('.//span[@class="rating_num"]/text()'),
#'time':item[4].strip()[5:],
}
'''
def writeFile(content):
'''执行文件追加写操作'''
#print(content)
with open("./result.txt",'a',encoding='utf-8') as f:
f.write(json.dumps(content,ensure_ascii=False) + "\n")
#json.dumps 序列化时对中文默认使用的ascii编码.想输出真正的中文需要指定ensure_ascii=False
def main(offset):
''' 主程序函数,负责调度执行爬虫处理 '''
url = 'https://movie.douban.com/top250?start=' + str(offset)
#print(url)
html = getPage(url)
#判断是否爬取到数据,并调用解析函数
if html:
for item in parsePage(html):
writeFile(item)
# 判断当前执行是否为主程序运行,并遍历调用主函数爬取数据
if __name__ == '__main__':
#main(0)
for i in range(10):
main(offset=i*25)
time.sleep(1)
16. 图片信息爬取实战
1. 案例分析
- 任务:爬取京东指定商品图片信息,并存储在当期目录下。
- url地址:https://list.jd.com/list.html?cat=9987,653,655
- 分析Web的响应内容,并作出对应处理准备:
2. 具体实现代码:
import requests
from bs4 import BeautifulSoup
from urllib.request import urlretrieve
# 定义请求url地址
url = "https://list.jd.com/list.html?cat=9987,653,655"
# 使用requests爬取指定url信息
res = requests.get(url)
#print(res.text)
# 使用BeautifulSoup创建解析器
soup = BeautifulSoup(res.text,"lxml")
# 解析里面的所有商品图片
imlist = soup.find_all(name="img",attrs={"width":"220","height":"220"})
#print(len(imlist))
# 遍历并解析里面的图片url地址信息
m=1
for im in imlist:
#首先判断有无src属性来决定如何获取
if 'src' in im.attrs:
imurl = "https:"+im.attrs['src']
else:
imurl = "https:"+im.attrs['data-lazy-img']
#储存图片(两种方式)
# 使用urllib中urlretrieve直接存储图片
urlretrieve(imurl,'./mypic/p'+str(m)+'.jpg')
'''
# 默认情况下,当您发出请求时,响应正文会立即下载,而设置stream参数为true,则只有响应头已经下载并且连接保持打开状态。
with requests.get(imurl, stream=True) as ir: # 使用with的好处不用考虑close关闭问题。
with open('./mypic/p'+str(m)+'.jpg', 'wb') as f:
for chunk in ir:
f.write(chunk)
'''
print('p'+str(m)+'.jpg')
m += 1
17. Fiddler抓包工具
1. Fiddler抓包工具:
- Fiddler(中文名称:小提琴)是一个HTTP的调试代理,以代理服务器的方式,监听系统的Http网络数据流动
- Fiddler是一个http协议调试代理工具,它能够记录并检查所有你的电脑和互联网之间的http通讯,设置断点,查看所有的“进出”Fiddler的数据(指cookie,html,js,css等文件,这些都可以让你胡乱修改的意思)。
- Fiddler 要比其他的网络调试器要更加简单,因为它不仅仅暴露http通讯还提供了一个用户友好的格式。
- 官方网址:https://www.telerik.com/fiddler
- 文档地址:http://docs.telerik.com/fiddler/configure-fiddler/tasks/configurefiddler
- 运行过程:
]
- 操作界面:
]
2. Fiddler配置过程:
- 安装好后打开fiddler→选择 Tools >Fildder Options > Https
]
- https 勾选下列选项
]
- Connection勾选允许远程连接
]
- 设置完成后点击下面的【OK】按钮保存
]
- 我们在刚开始进入Fildder的界面上,使用手机或者电脑网上冲浪就可以看到我们的http请求了。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-89JW1AsE-1603719522341)(https://edu.csdn.net/notebook/python/images/week03/77.jpg)]]
3. 使用fiddler抓取HTTPS协议数据与疑难杂症终极解决方案
18. 浏览器伪装技术实战
18.1 网站常见的反爬虫和应对方法
- 一般网站从三个方面反爬虫:
用户请求的Headers
,用户行为
,网站目录和数据加载方式
。 - 前两种比较容易遇到,大多数网站都从这些角度来反爬虫。第三种一些应用ajax的网站会采用,这样增大了爬取的难度。
① 通过Headers反爬虫
- 从用户请求的Headers反爬虫是最常见的反爬虫策略。
- 很多网站都会对Headers的User-Agent进行检测,还有一部分网站会对Referer进行检测(一些资源网站的防盗链就是检测Referer)。
- 对于检测Headers的反爬虫,在爬虫中修改或者添加Headers就能很好的绕过。
- 将浏览器的User-Agent复制到爬虫的Headers中;或者将Referer值修改为目标网站域名。
② 基于用户行为反爬虫
- 还有一部分网站是通过检测用户行为,例如同一IP短时间内多次访问同一页面,或者同一账户短时间内多次进行相同操作。
- 大多数网站都是前一种情况,对于这种情况,使用IP代理就可以解决。
- 对于第二种情况,可以在每次请求后随机间隔几秒再进行下一次请求。
- 有些有逻辑漏洞的网站,可以通过请求几次,退出登录,重新登录,继续请求来绕过同一账号短时间内不能多次进行相同请求的限制。
③ 动态页面的反爬虫
- 大多网站界面都是静态页面(即在浏览器中查看源代码可见),但是还有一部分网站中的数据是后期通过ajax请求(或其他方式如推送技术)得到。
- 解决办法:首先用Firebug或者Fiddler对网络请求进行分析。找到ajax的请求url,通过Python模拟请求得到需要的数据。
- 但是还有些网站把ajax请求的所有参数全部加密了,针对于这方式我们后面会给大家讲解
动态渲染页面信息爬取
。
18.2 请求头Headers介绍
1)请求(客户端->服务端[request])
GET(请求的方式) /newcoder/hello.html(请求的目标资源) HTTP/1.1(请求采用的协议和版本号)
Accept: */*(客户端能接收的资源类型)
Accept-Language: en-us(客户端接收的语言类型)
Connection: Keep-Alive(维护客户端和服务端的连接关系)
Host: localhost:8080(连接的目标主机和端口号)
Referer: http://localhost/links.asp(告诉服务器我来自于哪里)
User-Agent: Mozilla/4.0(客户端版本号的名字)
Accept-Encoding: gzip, deflate(客户端能接收的压缩数据的类型)
If-Modified-Since: Tue, 11 Jul 2000 18:23:51 GMT(缓存时间)
Cookie(客户端暂存服务端的信息)
Date: Tue, 11 Jul 2000 18:23:51 GMT(客户端请求服务端的时间)
2)响应(服务端->客户端[response])
HTTP/1.1(响应采用的协议和版本号) 200(状态码) OK(描述信息)
Location: http://www.baidu.com(服务端需要客户端访问的页面路径)
Server:apache tomcat(服务端的Web服务端名)
Content-Encoding: gzip(服务端能够发送压缩编码类型)
Content-Length: 80(服务端发送的压缩数据的长度)
Content-Language: zh-cn(服务端发送的语言类型)
Content-Type: text/html; charset=GB2312(服务端发送的类型及采用的编码方式)
Last-Modified: Tue, 11 Jul 2000 18:23:51 GMT(服务端对该资源最后修改的时间)
Refresh: 1;url=http://www.it315.org(服务端要求客户端1秒钟后,刷新,然后访问指定的页面路径)
Content-Disposition: attachment; filename=aaa.zip(服务端要求客户端以下载文件的方式打开该文件)
Transfer-Encoding: chunked(分块传递数据到客户端)
Set-Cookie:SS=Q0=5Lb_nQ; path=/search(服务端发送到客户端的暂存数据)
Expires: -1//3种(服务端禁止客户端缓存页面数据)
Cache-Control: no-cache(服务端禁止客户端缓存页面数据)
Pragma: no-cache(服务端禁止客户端缓存页面数据)
Connection: close(1.0)/(1.1)Keep-Alive(维护客户端和服务端的连接关系)
Date: Tue, 11 Jul 2000 18:23:51 GMT(服务端响应客户端的时间)
18.3 在requests中设置请求头Headers
import requests
# 代理IP地址
proxy = {'HTTP':'117.85.105.170:808','HTTPS':'117.85.105.170:808'}
# header头信息
headers = {
'Host': 'blog.csdn.net',
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:43.0) Gecko/20100101 Firefox/43.0',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language': 'zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3',
'Accept-Encoding': 'gzip, deflate',
'Referer': 'http://www.baidu.com',
'Connection': 'keep-alive',
'Cache-Control': 'max-age=0',
}
# 请求url地址
url = "http://blog.csdn.net"
# 提交请求爬取信息
response = requests.get(url,headers=headers,proxies=proxy)
# 获取响应码
print(response.status_code)
- 浏览器用户代理池信息
user_agents = [
'Mozilla/5.0 (Windows; U; Windows NT 5.1; it; rv:1.8.1.11) Gecko/20071127 Firefox/2.0.0.11',
'Opera/9.25 (Windows NT 5.1; U; en)',
'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)',
'Mozilla/5.0 (compatible; Konqueror/3.5; Linux) KHTML/3.5.5 (like Gecko) (Kubuntu)',
'Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.12) Gecko/20070731 Ubuntu/dapper-security Firefox/1.5.0.12',
'Lynx/2.8.5rel.1 libwww-FM/2.14 SSL-MM/1.4.1 GNUTLS/1.2.9',
'Mozilla/5.0 (X11; Linux i686) AppleWebKit/535.7 (KHTML, like Gecko) Ubuntu/11.04 Chromium/16.0.912.77 Chrome/16.0.912.77 Safari/535.7',
'Mozilla/5.0 (X11; Ubuntu; Linux i686; rv:10.0) Gecko/20100101 Firefox/10.0',
]
import random
agent = random.choice(user_agents) # 随机获取一个浏览器用户信息
19. Ajax信息爬取实战
- Ajax = 异步 JavaScript 和 XML(标准通用标记语言的子集)。
- Ajax 是一种用于创建快速动态网页的技术。
- Ajax 是一种在无需重新加载整个网页的情况下,能够更新部分网页的技术。
- 案例:爬取京东指定商品信息的评论信息
#爬取指定京东商品的评论信息
import requests
import re
# header头信息
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:43.0) Gecko/20100101 Firefox/43.0',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language': 'zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3',
'Accept-Encoding': 'gzip, deflate',
'Referer': 'http://www.baidu.com',
'Connection': 'keep-alive',
'Cache-Control': 'max-age=0',
}
#请求url地址
url = "https://sclub.jd.com/comment/productPageComments.action?callback=fetchJSON_comment98vv14&productId=6045059&score=0&sortType=5&page=1&pageSize=10&isShadowSku=4687958&rid=0&fold=1"
# 提交请求爬取信息
response = requests.get(url,headers=headers)
#获取响应码
html = response.text
pat = '"content":"(.*?)"'
items = re.findall(pat,html,re.S)
#print(items)
for v in items:
print(v)
print("="*80)
20. 验证码信息识别
- OCR 即Optical Character Recognition, 光学字符识别,是指通过扫描字符,然后通过其形状将其翻译成电子文本的过程。
- tesserocr 是Python的一个OCR识别库。GitHub:https://github.com/tesseract-ocr/tesseract
20.1 软件安装:
- 注意:在安装tesserocr前都需要先安装tesseract,具体说明如下:
pip install tesserocr #安装tesserocr
pip install pillow # 图片处理目录
注意:
在mac下安装tesserocr ,需要先安装下面的软件
brew install tesseract --all-languages
brew install imagemagick
在:Ubuntu、Debian 系统下先安装:
sudo apt-get intall -y tesseract-ocr libtesseract-dev libleptonica-dev
在:CentOS、Red Hat 系统:
yum install -y tesseract
在windows安装tesserocr前要先安装:tesseract 他是为tesserocr提供支持的
下载目录:https://digi.bib.uni-mannheim.de/tesseract/ 会有很多下载文件,下载一个3.0版本的exe文件即可
在安装中要勾选上Additional language data(download)选项来安装OCR识别支持的语言包
20.2 具体使用:
- 简单识别验证码
#验证识别测试
import tesserocr
from PIL import Image
#打开图片
image = Image.open("./code/5.png")
#识别验证码
result = tesserocr.image_to_text(image)
print(result)
- 验证码的深度处理,如转灰度、二值化等操作
#验证识别测试
import tesserocr
from PIL import Image
image = Image.open("./code/5.png")
#将图片转化为灰度图像
image = image.convert("L")
threshold = 127
table=[]
for i in range(256):
if i < threshold:
table.append(0)
else:
table.append(1)
image = image.point(table,'1')
#识别验证码
result = tesserocr.image_to_text(image)
print(result)
21. 网络爬虫阶段案例实战
- 任务:Ajax爬取今日头条的街拍美图
- 爬取url地址:https://www.toutiao.com/search_content/
1. 分析:
- 分析url地址:https://www.toutiao.com/search_content/? 每页20条数据,Ajax加载数据
- 需要提交参数:
params = {
'offset': offset, #页码数据
'format': 'json',
'keyword': '街拍', #搜索关键字
'autoload': 'true',
'count': '20',
'cur_tab': '3',
'from': 'gallery',
}
2. 具体实现代码如下:
import os,time
import requests
from urllib.parse import urlencode
from urllib.request import urlretrieve
def getPage(offset):
'''爬取指定url页面信息'''
params = {
'offset': offset,
'format': 'json',
'keyword': '街拍',
'autoload': 'true',
'count': '20',
'cur_tab': '3',
'from': 'gallery',
}
url = 'https://www.toutiao.com/search_content/?' + urlencode(params)
try:
response = requests.get(url)
if response.status_code == 200:
return response.json()
except requests.ConnectionError:
return None
def getImages(json):
'''解析获取图片信息'''
data = json.get('data')
if data:
for item in data:
# print(item)
image_list = item.get('image_list')
title = item.get('title')
# print(image_list)
for image in image_list:
yield {
'image': image.get('url'),
'title': title
}
def saveImage(item):
'''储存图片'''
# 处理每组图片的存储路径
path = os.path.join("./mypic/",item.get('title'))
if not os.path.exists(path):
os.mkdir(path)
# 拼装原图和目标图片的路径即名称
local_image_url = item.get('image')
image_url = "http:"+local_image_url.replace('list','large')
save_pic = path+"/"+local_image_url.split("/").pop()+".jpg"
# 使用urllib中urlretrieve直接存储图片
urlretrieve(image_url,save_pic)
def main(offset):
''' 主程序函数,负责调度执行爬虫处理 '''
json = getPage(offset)
for item in getImages(json):
print(item)
saveImage(item)
# 判断当前执行是否为主程序运行,并遍历调用主函数爬取数据
if __name__ == '__main__':
#main(0)
for i in range(5):
main(offset=i*20)
time.sleep(1)