爬虫简介
网络爬虫
爬虫指在使用程序模拟浏览器向服务端发出网络请求,以便获取服务端返回的内容。
但这些内容可能涉及到一些机密信息,所以爬虫领域目前来讲是属于灰色领域,切勿违法犯罪。
很多人学习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