python爬虫最全总结
Posted 南岸青栀*
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了python爬虫最全总结相关的知识,希望对你有一定的参考价值。
python爬虫–总结
前文回顾
python爬虫–类级别写法
python爬虫–爬取9某1看剧网电视剧
python爬虫–爬取网易云音乐评论
python爬虫–scrapy(再探)
python爬虫–scrapy(初识)
python爬虫–selenium模块
python爬虫–异步
文章目录
- python爬虫--总结
- 前文回顾
- python爬虫利器利器--BeautifulSoup
- python爬虫利器--lxml
- python爬虫--验证码,cookie
- python爬虫--并发
- python爬虫利器--selenium
- python爬虫利器--scrapy框架
- python爬虫面试题
requests模块中中的content和text的区别
- content用来返回二进制数据,适用于保存二进制数据,例如:图片,视频,文件等
- text适用于显示文本数据,编码根据服务器的响应来显示,也可以自己设置
requests.text是根据网页的响应来猜测编码,如果服务器不指定的话,默认编码是"ISO-8859-1"所以这是为什么有些时候用
response.text 返回的是乱码的原因
IO操作
写文件
open()方法
你必须先用Python内置的open()函数打开一个文件,创建一个file对象,相关的方法才可以调用它进行读写。
f = open('test.txt', 'w',encoding='utf-8')
f.write('Hello, python!')
f.close()
写文件时,操作系统往往不会立刻把数据写入磁盘,而是放到内存缓存起来,空闲的时候再慢慢写入。只有调用close()
方法时,操作系统才保证把没有写入的数据全部写入磁盘。忘记调用close()
的后果是数据可能只写了一部分到磁盘,剩下的丢失了
with open() as 方法
with open('test.txt','w') as f:
f.write('Hello, python!')
读文件
f = open('test.txt','r')
f.read()
f.close()
with open() as 方法
with open('test.txt','r') as f :
print(f.read())
模式 | 描述 |
---|---|
x | 写模式,新建一个文件,如果该文件已存在则会报错。 |
b | 二进制模式。 |
+ | 打开一个文件进行更新(可读可写)。 |
r | 以只读方式打开文件。文件的指针将会放在文件的开头。这是默认模式。 |
rb | 以二进制格式打开一个文件用于只读。文件指针将会放在文件的开头。这是默认模式。一般用于非文本文件如图片等。 |
r+ | 打开一个文件用于读写。文件指针将会放在文件的开头。 |
rb+ | 以二进制格式打开一个文件用于读写。文件指针将会放在文件的开头。一般用于非文本文件如图片等。 |
w | 打开一个文件只用于写入。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。 |
wb | 以二进制格式打开一个文件只用于写入。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。一般用于非文本文件如图片等。 |
w+ | 打开一个文件用于读写。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。 |
wb+ | 以二进制格式打开一个文件用于读写。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。一般用于非文本文件如图片等。 |
a | 打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。 |
ab | 以二进制格式打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。 |
a+ | 打开一个文件用于读写。如果该文件已存在,文件指针将会放在文件的结尾。文件打开时会是追加模式。如果该文件不存在,创建新文件用于读写。 |
ab+ | 以二进制格式打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。如果该文件不存在,创建新文件用于读写。 |
创建目录
判断目标
os.path.exists("goal")
判断目标是否存在
创建目录:
os.mkdir("file")
创建目录
举例:
if not os.path.exists('./file'):
os.mkdir('./file')
JSON模块
dump和dumps区别
简单说就是dump需要一个类似于文件指针的参数(并不是真的指针,可称之为类文件对象),可以与文件操作结合,也就是说可以将dict转成str然后存入文件中;而dumps直接给的是str,也就是将字典转成str。
- dump是将对象序列化并保存到文件中
- dumps是将对象序列化
dump函数:
json.dump(obj, fp, *, skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan=True, cls=None, indent=None, separators=None, default=None, sort_keys=False, **kw)
常用参数:
obj
: 表示是要序列化的对象。
fp
: 文件描述符,将序列化的str保存到文件中。json模块总是生成str对象,而不是字节对象;因此,fp.write()
必须支持str输入。
ensure_ascii
: 默认值为True,能将所有传入的非ASCII字符转义输出。如果ensure_ascii
为False,则这些字符将按原样输出。
dumps函数
dumps
函数不需要传文件描述符,其他的参数和dump函数的一样
load和loads反序列化方法,将json格式数据解码为python对象。
json.load(fp, *, cls=None, object_hook=None, parse_float=None, parse_int=None, parse_constant=None, object_pairs_hook=None, **kw)
fp: 文件描述符,将fp(.read()支持包含JSON文档的文本文件或二进制文件)反序列化为Python对象。
loads函数:
s
: 将s(包含JSON文档的str,bytes或bytearray实例)反序列化为Python对象。
encoding
: 指定一个编码的格式。
loads
也不需要文件描述符,其他参数的含义和load函数的一致。
get请求和post请求
GET产生一个TCP数据包;POST产生两个TCP数据包。
get请求
GET提交,请求的数据会附在URL之后(就是把数据放置在HTTP协议头中),以?分割URL和传输数据,多个参数用&连接。
对于GET方式的请求,浏览器会把http header和data一并发送出去,服务器响应200(返回数据)
post请求
POST,浏览器先发送header,服务器响应100 continue,浏览器再发送data,服务器响应200 ok(返回数据)
练习:
import requests,json,os
url = 'https://movie.douban.com/j/chart/top_list'
headers =
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (Khtml, like Gecko) Chrome/84.0.4147.105 Safari/537.36'
param =
'type': '5',
'interval_id': '100:90',
'action':'',
'start': '20',
'limit': '20'
response = requests.get(url=url,headers=headers,params=param)
data_list = response.json()
if not os.path.exists('./temp'):
os.mkdir('./temp')
fp = open('./temp/douban.json','w',encoding="utf-8")
json.dump(data_list,fp=fp,ensure_ascii=False)
re模块
常用方法
从字符串中寻找指定的字符串
1、match
re.match(pattern, string[, flags])
从首字母开始开始匹配,string如果包含pattern子串,则匹配成功,返回Match对象,失败则返回None,若要完全匹配,pattern要以$结尾,只返回第一个。
2、search
re.search(pattern, string[, flags])
若string中包含pattern子串,则返回Match对象,否则返回None,注意,如果string中存在多个pattern子串,只返回第一个。
3、findall(返回数组)
re.findall(pattern, string[, flags])
返回string中所有与pattern相匹配的全部字串,返回形式为数组。
4、finditer(返回迭代器)
re.findall(pattern, string[, flags])
返回string中所有与pattern相匹配的全部字串,返回形式为数组。
修饰符 | 描述 |
---|---|
re.I | 使匹配对大小写不敏感 |
re.L | 做本地化识别(localle-aware)匹配 |
re.M | 多行匹配,影响^ $ |
re.S | 匹配包括换行在内的所有字符 |
re.U | 根据Unicode字符集解析字符。影响\\w,\\W,\\b,\\B |
re.X | 该标志给予更灵活的格式 |
例:
ex = '<div class="thumb">.*?<img src="(.*?)" alt=.*?</div>'
ex_data = re.findall(ex,gate_text,re.S)
常用元字符
元字符 | 含义 |
---|---|
. | 匹配除换行符以外的任意字符 |
\\w | 匹配字母或数字或下划线 |
\\s | 匹配任意的空白符 |
\\d | 匹配数字 |
\\n | 匹配一个换行符 |
\\t | 匹配一个制表符 |
^ | 匹配字符串开始 |
$ | 匹配字符串结束 |
\\W | 匹配非字母or数字or下标 |
\\D | 匹配非数字 |
\\S | 匹配非空白符 |
a|b | 匹配字符a或b |
() | 匹配括号内的表达式,也表示一个组 |
[...] | 匹配字符组中的字符 |
[^..] | 匹配除字符组中的字符 |
量词
字符 | 含义 |
---|---|
* | 重复零次或者更多次 |
+ | 重复一次或者更多次 |
? | 重复零次或更多次 |
n | 重复n次 |
n, | 重复n次或更多次 |
n,m | 重复n次到m次 |
贪婪模式和惰性匹配
匹配模式 | 含义 |
---|---|
.* | 贪婪匹配 |
.*? | 惰性匹配 |
#爬取糗事百科图片例子
import re,os,requests
if __name__ == '__main__':
url = 'https://www.qiushibaike.com/imgrank/page/%d/'
headers =
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.61 Safari/537.36 Edg/83.0.478.37'
if not os.path.exists("./qiubai"):
os.mkdir("./qiubai")
n = int(input("请输入需要爬取的页数:"))
for pageNum in(1,n):
new_url = format(url%pageNum)
page_text = requests.get(url=new_url,headers=headers).content.decode("utf-8")
ex = '<div class="thumb">.*?<img src="(?P<src>.*?)" alt=.*?</div>'
ex_data = re.finditer(ex,page_text,re.S)
for src in ex_data:
src = "https:"+src.group("src")
img_data = requests.get(url=src,headers=headers).content
img_name = src.split('/')[-1]
img_path = './qiubai/' + img_name
with open(img_path,'wb') as fp:
fp.write(img_data)
print(img_name,"successful!!")
python爬虫利器利器–BeautifulSoup
Beautiful Soup提供一些简单的、python式的函数用来处理导航、搜索、修改分析树等功能。他是一个工具箱,通过解析文档为用户提供需要抓取的数据。
BeautifulSoup简单操作
from bs4 import Beautifulsoup
#创建Beautiful Soup对象
soup = Beautifulsoup(html)
#也可以使用本地HTML文件来创建对象
soup = Beautifulsoup(open('index.html'))
#打印出soup对象的内容,格式化输出
print(soup.prettify())
BeautifulSoup四大对象种类
BeautifulSoup将负责HTML文档转换成一个复杂的树形结构,每个节点都是python对象,所有对象都可归结为4种:
- Tag
- NavigableString
- BeautifulSoup
- Comment
(1)Tag
Tag就是HTML中的一个个标签
print(soup.title)
#<title>The Dormouse's story</title>
=====================================
print(soup.head)
#<head><title>The Dormouse's story</title></head>
=====================================
print(soup.a)
#<a class="sister" href="http://example.com/elsie" id="link1"><!-- Elsie --></a>
=====================================
print(soup.p)
#<p class="title" name="dromouse"><b>The Dormouse's story</b></p>
#验证标签对象的类型
print(type(soup.a))
#<class 'bs4,element.Tag'>
Tag两个重要的属性:name、attrs
print(soup.name)
print(soup.head.name)
#[document]
#head
=====================================
print(soup.pattrs)
#'class':['title'],'name':'dromouse'
name:对于其他内部标签,输出的值变为标签本身的名字(soup对象的name为[document])
attrs:列出当前标签的所有属性,得到的类型是一个字典
#如果想要单独获取某个属性
print(soup.p['class'])
print(soup.p.get('class'))
#['title']
(2)NavigablesString
获取标签内部的文字。
print(soup.p.string)
#The Dormouse's story
#验证navigablestring的类型
print(type(soup.pstring))
#<class 'bs4.element.Navigablestring'>
(3)Beautifulsoup
Beautifulsoup对象表示的是一个文档的全部内容。大部分时候,可以把它当做Tag对象,是一个特殊的Tag。
print(type(soup.name))
#<type 'unicode'>
print(soup.name)
#[document]
print(soup.attrs)
#空字典
(4)comment
comment对象使用一个特殊类型的Navugablestring对象
遍历文档树
(1)直接子节点
.contents.children属性
.children返回的不是list,不过我们可以通过遍历获取所有子节点。
print(soup.head.children)
#是一个list生成器对象
#<listiterator object at 0x7f71457f5710>
for child in soup.body.children:
print child
<p class="title" name="dromouse"><b>The Dormouse's story</b></p>
<p class="story">Once upon a time there were three little sisters; and their names were
<p class="story">...</p>
(2)所有子孙节点
.descendants属性
.descendants .contents 和 .children 属性仅包含 tag 的直接子节点,.descendants 属性可以对所有 tag 的子孙节点进行递归循环,和 children 类似,我们也需要遍历获取其中的内容
for child in soup.descendants:
print child
运行之后可以发现,是所有节点都被打印出来了。
(3)节点内容
.string 属性
如果一个标签里面没有标签了,那么 .string 就会返回标签里面的内容。如果标签里面只有唯一的一个标签了,那么 .string 也会返回最里面的内容。
如果tag包含了多个子节点,tag就无法确定,string方法应该调用那些子节点的内容,.string的输出结果为None
(4)多个内容
.strings .stripped_strings 属性
.strings 获取多个内容,不过需要遍历获取
for string in soup.strings:
print(repr(string))
# u"The Dormouse's story"
# u'\\n\\n'
# u"The Dormouse's story"
# u'\\n\\n'
使用 .stripped_strings 可以去除多余空白内容
for string in soup.stripped_strings:
print(repr(string))
# u"The Dormouse's story"
# u"The Dormouse's story"
# u'Once upon a time there were three little
(5)父节点
.parent 属性
p = soup.p
print p.parent.name
#body
(6)全部父节点
.parents 属性
通过元素的 .parents 属性可以递归得到元素的所有父辈节点
content = soup.head.title.string
for parent in content.parents:
print parent.name
(7)兄弟节点
.next_sibling .previous_sibling 属性
.next_sibling 属性获取了该节点的下一个兄弟节点,.previous_sibling 则与之相反,如果节点不存在,则返回 None 注意:实际文档中的 tag 的 .next_sibling 和 .previous_sibling 属性通常是字符串或空白,因为空白或者换行也可以被视作一个节点,所以得到的结果可能是空白或者换行。
(8)全部兄弟节点
.next_siblings .previous_siblings 属性
通过 .next_siblings 和 .previous_siblings 属性可以对当前节点的兄弟节点迭代输出
(9)前后节点
.next_element .previous_element 属性
与 .next_sibling .previous_sibling 不同,它并不是针对于兄弟节点,而是在所有节点,不分层次。
(10)所有前后节点
.next_elements .previous_elements 属性
通过 .next_elements 和 .previous_elements 的迭代器就可以向前或向后访问文档的解析内容,就好像文档正在被解析一样
搜索文档树
(1)find_all(name,attrs,recursive,text,**kwargs)
find_all () 方法搜索当前 tag 的所有 tag 子节点,并判断是否符合过滤器的条件
1)name 参数 name 参数可以查找所有名字为 name 的 tag, 字符串对象会被自动忽略掉
-
- A. 传字符串 最简单的过滤器是字符串。在搜索方法中传入一个字符串参数,Beautiful Soup 会查找与字符串完整匹配的内容
soup.find_all('b')
# [<b>The Dormouse's story</b>]
-
- B. 传正则表达式 如果传入正则表达式作为参数,Beautiful Soup 会通过正则表达式的 match () 来匹配内容。下面例子中找出所有以 b 开头的标签,这表示 和标签都应该被找到
import re
for tag in soup.find_all(re.compile("^b")):
print(tag.name)
# body
# b
-
- C. 传列表 如果传入列表参数,Beautiful Soup 会将与列表中任一元素匹配的内容返回。下面代码找到文档中所有标签和标签
soup.find_all(["a", "b"])
# [<b>The Dormouse's story</b>,
# <a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
# <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>,
# <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]
-
- D. 传 True True 可以匹配任何值,下面代码查找到所有的 tag, 但是不会返回字符串节点
for tag in soup.find_all(True):
print(tag.name)
# html
# head
# title
# body
# p
# b
# p
# a
# a
-
- E. 传方法 如果没有合适过滤器,那么还可以定义一个方法,方法只接受一个元素参数 [4] , 如果这个方法返回 True 表示当前元素匹配并且被找到,如果不是则反回 False 下面方法校验了当前元素,如果包含 class 属性却不包含 id 属性,那么将返回 True
- 2)keyword 参数
如果一个指定名字的参数不是搜索内置的参数名,搜索时会把该参数当作指定名字 tag 的属性来搜索,如果包含一个名字为 id 的参数,Beautiful Soup 会搜索每个 tag 的”id” 属性.
soup.find_all(id='link2')
# [<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>]
如果传入 href 参数,Beautiful Soup 会搜索每个 tag 的”href” 属性
soup.find_all(href=re.compile("elsie"))
# [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>]
(2)find(name,attrs,recursive,text,**kwargs)
它与 find_all () 方法唯一的区别是 find_all () 方法的返回结果是值包含一个元素的列表,而 find () 方法直接返回结果。
(3)find_parents() find_parent()
find_all () 和 find () 只搜索当前节点的所有子节点,孙子节点等. find_parents () 和 find_parent () 用来搜索当前节点的父辈节点,搜索方法与普通 tag 的搜索方法相同,搜索文档搜索文档包含的内容
python爬虫利器–lxml
lxml用法
首先使用lxml的etree库,然后利用etree.HTML初始化,然后我们将其打印出来。lxml的一个非常实用的功能就是自动修正html代码。
文件读取
处理直接读取字符串,还支持从文件读取内容。
Xpath基本用法
- /:表示的是从根节点开始定位。表示一个层级
- //:表示多个层级。可以表示从任意位置开始定位
- 属性定位://div[@class='song'] tag[@attrName='attrValue']
- 索引定位://div[@class='song']/p[3] 索引从1开始的
- 取文本:
- /text()获取的是标签中直系的文本内容
- //text()标签中非直系的文本内容(所有文本内容)
- 取属性:
/@attrName ==>img/src
(1)获取某个标签的内容
获取a标签的所有内容,a后面就不用再加正斜杠,否则报错。
做法1:
html = etree.HTML(wb_data)
html_data = html.xpath('/html/body/div/ul/li/a')
print(html)
for i in html_data:
print(i.text)
<Element html at 0x12fe4b8>
first item
second item
third item
fourth item
fifth item
做法2:直接在需要查找内容的标签后面加上一个/text()
html = etree.HTML(wb_data)
html_data = html.xpath('/html/body/div/ul/li/a/text()')
print(html)
for i in html_data:
print(i)
<Element html at 0x138e4b8>
first item
second item
third item
fourth item
fifth item
(2)打印指定路径下a标签的属性
html = etree最全Python爬虫总结
PHP爬虫最全总结2-phpQuery,PHPcrawer,snoopy框架中文介绍