python爬虫篇,零基础学爬虫之精华版

本文是一份Python爬虫教程的精华版,涵盖了爬虫基础、requests模块、HTML与XPath解析、Selenium自动化以及Scrapy框架等内容。强调了合法合规使用爬虫的重要性,并提供了实例讲解了如何处理请求、响应、数据解析和存储。同时,介绍了Scrapy的基本架构和项目设置,以及使用中间件进行请求和响应的处理。文章还探讨了如何应对网站的反爬策略,如代理和验证码的处理。
摘要由CSDN通过智能技术生成

爬虫简介

网络爬虫

爬虫指在使用程序模拟浏览器向服务端发出网络请求,以便获取服务端返回的内容。

但这些内容可能涉及到一些机密信息,所以爬虫领域目前来讲是属于灰色领域,切勿违法犯罪。

很多人学习python,不知道从何学起。
很多人学习python,掌握了基本语法过后,不知道在哪里寻找案例上手。
很多已经做案例的人,却不知道如何去学习更加高深的知识。
那么针对这三类人,我给大家提供一个好的学习平台,免费领取视频教程,电子书籍,以及课程的源代码!??¤
QQ群:232030553

爬虫本身作为一门技术没有任何问题,关键是看人们怎么去使用它

《中华人民共和国刑法》第二百八十五条规定:非法获取计算机信息系统数据、非法控制计算机信息系统罪,是指违反国家规定, 侵入国家事务、国防建设、尖端科学技术领域以外的计算机信息系统或者采用其他技术手段,获取该计算机信息系统中存储、处理或者传输的数据 ,情节严重的行为。刑法第285条第2款明确规定,犯本罪的, 处三年以下有期徒刑或者拘役,并处或者单处罚金;情节特别严重的,处三年以上七年以下有期徒刑,并处罚金。

《反不正当竞争法》第九条规定:以不正当手段获取他人商业秘密的行为即已经构成侵犯商业秘密。而后续如果进一步利用,或者公开该等信息,则构成对他人商业秘密的披露和使用,同样构成对权利人的商业秘密的侵犯。

《刑法》第二百八十六条规定:违反国家规定,对计算机信息系统功能进行删除、修改、增加、干扰,造成计算机信息系统不能正常运行,后果严重的,构成犯罪,处五年以下有期徒刑或者拘役;后果特别严重的,处五年以上有期徒刑。而违反国家规定,对计算机信息系统中存储、处理或者传输的数据和应用程序进行删除、修改、增加的操作,后果严重的,也构成犯罪,依照前款的规定处罚。

《网络安全法》第四十四条规定:任何个人和组织不得窃取或者以其他非法方式获取个人信息。因此,如果爬虫在未经用户同意的情况下大量抓取用户的个人信息,则有可能构成非法收集个人信息的违法行为。

《民法总则》第111条规定:任何组织和个人需要获取他人个人信息的,应当依法取得并确保信息安全。不得非法收集、使用、加工、传输他人个人信息

爬虫分类

根据爬虫的应用范畴,可有一些三种区分:

通用爬虫

搜索引擎本质就是一个巨大的爬虫,首先该爬虫会爬取整张页面,并且对该页面做备份,之后对其进行数据内容处理如抓取关键字等,然后向用户提供检索接口。

聚焦式爬虫

只关注于页面上某一部分内容,如只关注图片、链接等。

增量式爬虫

用于检索内容是否更新,如开发了一个增量式爬虫每天查看一下云崖博客有没有更新,有更新就爬下来等等...

robots协议

robots 协议是爬虫领域非常出名的一种协议,由门户网站提供。

它规定了该站点哪些内容允许爬取,哪些内容不允许爬取。

如果爬取不允许的内容,可对其追究法律责任。

requests模块

requests 模块是 Python 中发送网络请求的一款非常简洁、高效的模块。

pip install requests

发送请求

支持所有的请求方式:

import requests

requests.get("https://www.python.org/")
requests.post("https://www.python.org/")
requests.put("https://www.python.org/")
requests.patch("https://www.python.org/")
requests.delete("https://www.python.org/")
requests.head("https://www.python.org/")
requests.options("https://www.python.org/")

# 指定请求方式
requests.request("get","https://www.python.org/")

当请求发送成功后,会返回一个 response 对象。

get请求

基本的 get 请求参数如下:

参数 描述
params 字典,get请求的参数,value支持字符串、字典、字节(ASCII编码内)
headers 字典,本次请求携带的请求头
cookies 字典,本次请求携带的cookies

演示如下:

import requests

res = requests.get(
    url="http://127.0.0.1:5000/index",
    params={"key": "value"},
    cookies={"key": "value"},
)

print(res.content)

post请求

基本的 post 请求参数如下:

参数 描述
data 字典,post请求的参数,value支持文件对象、字符串、字典、字节(ASCII编码内)
headers 字典,本次请求携带的请求头
cookies 字典,本次请求携带的cookies

演示如下:

import requests

res = requests.post(
    url="http://127.0.0.1:5000/index",
    # 依旧可以携带 params
    data={"key": "value"},
    cookies={"key": "value"},
)

print(res.content)

高级参数

更多参数:

参数 描述
json 字典,传入json数据,将自动进行序列化,支持get/post,请求体传递
files 字典,传入文件对象,支持post
auth 认证,传入HTTPDigestAuth对象,一般场景是路由器弹出的两个输入框,爬虫获取不到,将用户名和密码输入后会base64加密然后放入请求头中进行交给服务端,base64("名字:密码"),请求头名字:authorization
timeout 超时时间,传入float/int/tuple类型。如果传入的是tuple,则是 (链接超时、返回超时)
allow_redirects 是否允许重定向,传入bool值
proxies 开启代理,传入一个字典
stream 是否返回文件流,传入bool值
cert 证书地址,这玩意儿来自于HTTPS请求,需要传入该网站的认证证书地址,通常来讲如果是大公司的网站不会要求这玩意儿

演示:

def param_method_url():
    # requests.request(method='get', url='http://127.0.0.1:8000/test/')
    # requests.request(method='post', url='http://127.0.0.1:8000/test/')
    pass


def param_param():
    # - 可以是字典
    # - 可以是字符串
    # - 可以是字节(ascii编码以内)

    # requests.request(method='get',
    # url='http://127.0.0.1:8000/test/',
    # params={'k1': 'v1', 'k2': '水电费'})

    # requests.request(method='get',
    # url='http://127.0.0.1:8000/test/',
    # params="k1=v1&k2=水电费&k3=v3&k3=vv3")

    # requests.request(method='get',
    # url='http://127.0.0.1:8000/test/',
    # params=bytes("k1=v1&k2=k2&k3=v3&k3=vv3", encoding='utf8'))

    # 错误
    # requests.request(method='get',
    # url='http://127.0.0.1:8000/test/',
    # params=bytes("k1=v1&k2=水电费&k3=v3&k3=vv3", encoding='utf8'))
    pass


def param_data():
    # 可以是字典
    # 可以是字符串
    # 可以是字节
    # 可以是文件对象

    # requests.request(method='POST',
    # url='http://127.0.0.1:8000/test/',
    # data={'k1': 'v1', 'k2': '水电费'})

    # requests.request(method='POST',
    # url='http://127.0.0.1:8000/test/',
    # data="k1=v1; k2=v2; k3=v3; k3=v4"
    # )

    # requests.request(method='POST',
    # url='http://127.0.0.1:8000/test/',
    # data="k1=v1;k2=v2;k3=v3;k3=v4",
    # headers={'Content-Type': 'application/x-www-form-urlencoded'}
    # )

    # requests.request(method='POST',
    # url='http://127.0.0.1:8000/test/',
    # data=open('data_file.py', mode='r', encoding='utf-8'), # 文件内容是:k1=v1;k2=v2;k3=v3;k3=v4
    # headers={'Content-Type': 'application/x-www-form-urlencoded'}
    # )
    pass


def param_json():
    # 将json中对应的数据进行序列化成一个字符串,json.dumps(...)
    # 然后发送到服务器端的body中,并且Content-Type是 {'Content-Type': 'application/json'}
    requests.request(method='POST',
                     url='http://127.0.0.1:8000/test/',
                     json={'k1': 'v1', 'k2': '水电费'})


def param_headers():
    # 发送请求头到服务器端
    requests.request(method='POST',
                     url='http://127.0.0.1:8000/test/',
                     json={'k1': 'v1', 'k2': '水电费'},
                     headers={'Content-Type': 'application/x-www-form-urlencoded'}
                     )


def param_cookies():
    # 发送Cookie到服务器端
    requests.request(method='POST',
                     url='http://127.0.0.1:8000/test/',
                     data={'k1': 'v1', 'k2': 'v2'},
                     cookies={'cook1': 'value1'},
                     )
    # 也可以使用CookieJar(字典形式就是在此基础上封装)
    from http.cookiejar import CookieJar
    from http.cookiejar import Cookie

    obj = CookieJar()
    obj.set_cookie(Cookie(version=0, name='c1', value='v1', port=None, domain='', path='/', secure=False, expires=None,
                          discard=True, comment=None, comment_url=None, rest={'HttpOnly': None}, rfc2109=False,
                          port_specified=False, domain_specified=False, domain_initial_dot=False, path_specified=False)
                   )
    requests.request(method='POST',
                     url='http://127.0.0.1:8000/test/',
                     data={'k1': 'v1', 'k2': 'v2'},
                     cookies=obj)


def param_files():
    # 发送文件
    # file_dict = {
    # 'f1': open('readme', 'rb')
    # }
    # requests.request(method='POST',
    # url='http://127.0.0.1:8000/test/',
    # files=file_dict)

    # 发送文件,定制文件名
    # file_dict = {
    # 'f1': ('test.txt', open('readme', 'rb'))
    # }
    # requests.request(method='POST',
    # url='http://127.0.0.1:8000/test/',
    # files=file_dict)

    # 发送文件,定制文件名
    # file_dict = {
    # 'f1': ('test.txt', "hahsfaksfa9kasdjflaksdjf")
    # }
    # requests.request(method='POST',
    # url='http://127.0.0.1:8000/test/',
    # files=file_dict)

    # 发送文件,定制文件名
    # file_dict = {
    #     'f1': ('test.txt', "hahsfaksfa9kasdjflaksdjf", 'application/text', {'k1': '0'})
    # }
    # requests.request(method='POST',
    #                  url='http://127.0.0.1:8000/test/',
    #                  files=file_dict)

    pass


def param_auth():
	# 认证,浏览器BOM对象弹出对话框
	# 在HTML文档中是找不到该标签的,所以需要用这个对其进行传入,一般来说常见于路由器登录页面
    from requests.auth import HTTPBasicAuth, HTTPDigestAuth

    ret = requests.get('https://api.github.com/user', auth=HTTPBasicAuth('wupeiqi', 'sdfasdfasdf'))
    print(ret.text)

    # ret = requests.get('http://192.168.1.1',
    # auth=HTTPBasicAuth('admin', 'admin'))
    # ret.encoding = 'gbk'
    # print(ret.text)

    # ret = requests.get('http://httpbin.org/digest-auth/auth/user/pass', auth=HTTPDigestAuth('user', 'pass'))
    # print(ret)
    #


def param_timeout():
	# 超时时间,如果链接时间大于1秒就返回
    # ret = requests.get('http://google.com/', timeout=1)
    # print(ret)
	# 如果链接时间大于5秒就返回,或者响应时间大于1秒就返回
    # ret = requests.get('http://google.com/', timeout=(5, 1))
    # print(ret)
    pass


def param_allow_redirects():
    ret = requests.get('http://127.0.0.1:8000/test/', allow_redirects=False)
    print(ret.text)


def param_proxies():
	# 配置代理
    # proxies = {
    # "http": "61.172.249.96:80",
    # "https": "http://61.185.219.126:3128",
    # }

    # proxies = {'http://10.20.1.128': 'http://10.10.1.10:5323'}

    # ret = requests.get("http://www.proxy360.cn/Proxy", proxies=proxies)
    # print(ret.headers)


    # from requests.auth import HTTPProxyAuth
    #
    # proxyDict = {
    # 'http': '77.75.105.165',
    # 'https': '77.75.105.165'
    # }
    # auth = HTTPProxyAuth('username', 'mypassword')
    #
    # r = requests.get("http://www.google.com", proxies=proxyDict, auth=auth)
    # print(r.text)

    pass


def param_stream():
	# 文件流,直接写入文件即可
    ret = requests.get('http://127.0.0.1:8000/test/', stream=True)
    print(ret.content)
    ret.close()

    # from contextlib import closing
    # with closing(requests.get('http://httpbin.org/get', stream=True)) as r:
    # # 在此处理响应。
    # for i in r.iter_content():
    # print(i)

session对象

如果爬取一个网站,该网站可能会返回给你一些 cookies ,对这个网站后续的请求每次都要带上这些 cookies 比较麻烦。

所以可以直接使用 session 对象(自动保存 cookies )发送请求,它会携带当前对象中所有的 cookies 。

def requests_session():
    import requests
	# 使用session时,会携带该网站中所返回的所有cookies发送下一次请求。
	
	# 生成session对象
    session = requests.Session()

    ### 1、首先登陆任何页面,获取cookie

    i1 = session.get(url="http://dig.chouti.com/help/service")

    ### 2、用户登陆,携带上一次的cookie,后台对cookie中的 gpsd 进行授权
    i2 = session.post(
        url="http://dig.chouti.com/login",
        data={
            'phone': "8615131255089",
            'password': "xxxxxx",
            'oneMonth': ""
        }
    )

    i3 = session.post(
        url="http://dig.chouti.com/link/vote?linksId=8589623",
    )
    print(i3.text)

response对象

以下是 response 对象的所有参数:

参数 描述
response.text 返回文本响应内容
response.content 返回二进制响应内容
response.json 如果返回内容是json格式,则进行序列化
response.encoding 返回响应内容的编码格式
response.status_code 状态码
response.headers 返回头
response.cookies 返回的cookies对象
response.cookies.get_dict() 以字典形式展示返回的cookies对象
response.cookies.items() 以元组形式展示返回的cookies对象
response.url 返回的url地址
response.history 这是一个列表,如果请求被重定向,则将上一次被重定向的response对象添加到该列表中

编码问题

并非所有网页都是 utf8 编码,有的网页是 gbk 编码。

此时如果使用 txt 查看响应内容就要指定编码格式:

import requests
response=requests.get('http://www.autohome.com/news')
response.encoding='gbk'
print(response.text)

下载文件

使用 response.context 时,会将所有内容存放至内存中。

如果访问的资源是一个大文件,而需要对其进行下载时,可使用如下方式生成迭代器下载:

import requests

response=requests.get('http://bangimg1.dahe.cn/forum/201612/10/200447p36yk96im76vatyk.jpg')
with open("res.png","wb") as f:
    for line in response.iter_content():
        f.write(line)

json返回内容

如果确定返回内容是 json 数据,则可以通过 response.json 进行查看:

import requests
response = requests.get("http://127.0.0.1:5000/index")
print(response.json())

历史记录

如果访问一个地址却被重定向了,被重定向的地址会被存放到 response.history 这个列表中:

import requests
r = requests.get('http://127.0.0.1:5000/index')  # 被重定向了
print(r.status_code)  # 200
print(r.url)  # http://127.0.0.1:5000/new  # 重定向的地址
print(r.history)
# [<Response [302]>]

如果在请求时,指定 allow_redirects 参数为 False ,则禁止重定向:

import requests
r = requests.get('http://127.0.0.1:5000/index',allow_redirects=False)  # 禁止重定向
print(r.status_code)  # 302
print(r.url)  # http://127.0.0.1:5000/index
print(r.history)
# []

bs4模块

request 模块可以发送请求,获取 HTML 文档内容。

而 bs4 模块可以解析出 HTML 与 XML 文档的内容,如快速查找标签等等。

pip3 install bs4

bs4模块只能在Python中使用

bs4 依赖解析器,虽然有自带的解析器,但是目前使用最多的还是 lxml :

pip3 install lxml

基本使用

将 request 模块请求回来的 HTML 文档内容转换为 bs4 对象,使用其下的方法进行查找:

如下示例,解析出虾米音乐中的歌曲,歌手,歌曲时长:

import requests
from bs4 import BeautifulSoup
from prettytable import PrettyTable

# 实例化表格
table = PrettyTable(['编号', '歌曲名称', '歌手', '歌曲时长'])

url = r"https://www.xiami.com/list?page=1&query=%7B%22genreType%22%3A1%2C%22genreId%22%3A%2220%22%7D&scene=genre&type=song"
headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36",
}
response = requests.get(url=url, headers=headers)

# step01: 将文本内容实例化出bs对象
soup_obj = BeautifulSoup(response.text, "lxml")

# step02: 查找标签
main = soup_obj.find("div", attrs={"class": "table idle song-table list-song"})

# step03: 查找存放歌曲信息的tbody标签
tbody = main.select(".table-container>table>tbody")[0]

# step04: tbody标签中的每个tr都是一首歌曲
tr = tbody.find_all("tr")

# step04: 每个tr里都存放有歌曲信息,所以直接循环即可
for music in tr:
    name = music.select(".song-name>a")[0].text
    singer = music.select(".COMPACT>a")[0].text
    time_len = music.select(".duration")[0].text
    table.add_row([tr.index(music) + 1, name, singer, time_len])

# step05: 打印信息
print(table)

结果如下:

+------+--------------------------------------------------+--------------------+----------+
| 编号 |                     歌曲名称                     |        歌手        | 歌曲时长 |
+------+--------------------------------------------------+--------------------+----------+
|  1   | Love Story (Live from BBC 1's Radio Live Lounge) |    Taylor Swift    |  04:25   |
|  2   |                Five Hundred Miles                |        Jove        |  03:27   |
|  3   |    I'm Gonna Getcha Good! (Red Album Version)    |    Shania Twain    |  04:30   |
|  4   |                     Your Man                     |    Josh Turner     |  03:45   |
|  5   |             Am I That Easy To Forget             |     Jim Reeves     |  02:22   |
|  6   |                   Set for Life                   |    Trent Dabbs     |  04:23   |
|  7   |                    Blue Jeans                    |  Justin Rutledge   |  04:25   |
|  8   |                    Blind Tom                     | Grant-Lee Phillips |  02:59   |
|  9   |                      Dreams                      |   Slaid Cleaves    |  04:14   |
|  10  |                  Remember When                   |    Alan Jackson    |  04:31   |
|  11  |                Crying in the Rain                |    Don Williams    |  03:04   |
|  12  |                    Only Worse                    |    Randy Travis    |  02:53   |
|  13  |                     Vincent                      | The Sunny Cowgirls |  04:22   |
|  14  |           When Your Lips Are so Close            |    Gord Bamford    |  03:02   |
|  15  |                  Let It Be You                   |    Ricky Skaggs    |  02:42   |
|  16  |                  Steal a Heart                   |    Tenille Arts    |  03:09   |
|  17  |                      Rylynn                      |     Andy McKee     |  05:13   |
|  18  |        Rockin' Around The Christmas Tree         |     Brenda Lee     |  02:06   |
|  19  
  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值