python爬虫最全总结

Posted 南岸青栀*

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了python爬虫最全总结相关的知识,希望对你有一定的参考价值。

python爬虫–总结

前文回顾

python爬虫–类级别写法

python爬虫–爬取9某1看剧网电视剧

python爬虫–爬取网易云音乐评论

python爬虫–scrapy(再探)

python爬虫–scrapy(初识)

python爬虫–selenium模块

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

以上是关于python爬虫最全总结的主要内容,如果未能解决你的问题,请参考以下文章

最全Python爬虫总结

最全Python爬虫总结(转载)

史上最最最最最最最最全Python爬虫总结

PHP爬虫最全总结2-phpQuery,PHPcrawer,snoopy框架中文介绍

全网最全python爬虫系统进阶学习(附原代码)学完可就业

全网最全python爬虫系统进阶学习(附原代码)学完可就业