网络爬虫(又被称为网页蜘蛛、网络机器人),是一种按照一定的规则,自动抓取互联网信息的程序或者脚本。
直译为机器人排除协议,又可称为爬虫协议、机器人协议,是指网站所有者通过一个置于网站根目录下的文本文件,即robots.txt,告知搜索引擎的网络机器人(或称网络爬虫、网络蜘蛛)哪些网页不应被抓取,哪些网页可以抓取。
curl https://www.aaa.com/rebots.txt
==================================================================
User-agent: * #所有爬虫,如百度、谷歌、必应
Disallow: /subject_search #禁止访问 /subject_search
Disallow: /amazon_search
Disallow: /search
Allow: /ads.txt #允许访问 /ads.txt
User-agent: Wandoujia Spider #如果是豌豆荚爬虫
Disallow: / #禁止访问所有页面(完全屏蔽)
==================================================================
Requests模块是第三方模块,需要pip安装,requests模块在python内置模块的基础上进行了高度的封装,
从而使得python进行网络请求时,变得更加简洁和人性化。
import requests
payload = {
'aaa': 'AAA',
'bbb': ['ccc', 'ddd']
}
headers = {
'Content-Type': 'text/html;charset=utf-8',
'User-Agent' : 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)'
}
timeout = 5 # 超时时间的 单位为 秒
res = requests.get('http://www.baidu.com/', params=payload,headers=headers,timeout=timeout)
# 关闭请求
res.close()
# 返回的 res 为 Response对象:
'''
res.url #打印输出该 URL ==> http://www.baidu.com/?bbb=ccc&bbb=ddd&aaa=AAA
res.headers #以字典对象存储服务器响应头,但是这个字典比较特殊,字典键不区分大小写,若键不存在则返回None
res.status_code #返回连接状态,200正常。
res.text #默认以unicode形式返回网页内容,也就是网页源码的字符串。
res.content #以字节形式(二进制)返回。字节方式的响应体,会自动为你解码 gzip 和 deflate 压缩。
res.json() #把网页中的json数据转成字典并将其返回。
res.encoding #获取当前的编码
res.encoding = 'ISO-8859-1' #指定编码,res.text返回的数据类型,写在r.text之前。
'''
HTTP 协议规定 POST 提交的数据必须放在 请求体 中,但协议并没有规定数据必须使用什么编码方式,
服务端通过是根据请求头中的Content-Type
字段来获知请求中的消息主体是用何种方式进行编码,
再对消息主体进行解析。具体的编码方式包括:
payload = {
'key1': 'value1',
'key2': 'value2'
}
res = requests.post("http://httpbin.org/post", data=payload)
print(res.text)
# 关闭请求
res.close()
'''
"form": {
"key1": "value1",
"key2": "value2"
},
'''
payload = {
'key1': 'value1',
'key2': 'value2'
}
res = requests.post("http://httpbin.org/post", data=json.dumps(payload))
print(res.text)
# 关闭请求
res.close()
post 发送 文件
with open('666.jpg', 'rb') as file:
files = {'filess': file}
r = R.post(gugulocal, files=files)
print(r.text)
cookie 的处理主要分为两步,一、请求登录接口获取cookie 二、带着cookie进行后面的请求
此时就可以用到session了,session可以认为是进行一连串的请求,在这个过程中cookie不会丢失
所以我们称 session 会话。
import requests
session = requests.session()
# 1.登录
res = session.post(url, data={
"loginName":"111111",
"password":"111111",
})
print(res.cookies) # 查看cookies
# 2.获取数据
res2 = session.get(url)
先手动在网址进行登录,然后 获取 登录之后 的浏览器中存储的Cookie值,
将该属性值加入到 获取数据请求的代码中
这种方式一般用于登录的验证操作比较严格、验证码比较复杂的网站。
# 方式一
res = r.get("http://localhost:8888/ping", cookies={
"name":"wtt",
"age":"110",
})
# 方式二(方式一的本质实现)
res = requests.get("http://localhost:8888/ping", headers={
"Cookie":"name=wtt;age=110",
})
右键选择 网页源代码 查看到的内容就是 requests请求的res.text内容,
而往往有些网站对于资源的处理是后加式
处理,例如我们想获取一个视频连接,
而 网页源代码中 是没有 video 标签,而是 通过js的二次请求 将video追加到 网页源代码 中的,
这样 即使我们 通过 F12-Element 选项可以看到 video 标签,但是res.text内容中是没有的。
所以我们要 找到 二次 资源请求的接口地址,通过 F12-Network-XHR,便可以检测到。
XHR全称XMLHttpRequest,是一组API函数集,可被JavaScript、JScript、VBScript以及其它web浏览器内嵌的脚本语言调用,
通过HTTP在浏览器和web服务器之间收发XML或其它数据。XMLHTTP最大的好处在于可以动态地更新网页
,
它无需重新从服务器读取整个网页,也不需要安装额外的插件。该技术被许多网站使用,以实现快速响应的动态网页应用。
先 通过 F12-Element 找到video标签,进而 获取到的 真正的 视频链接地址,
然后再通过 F12-Network-XHR 找到接口返回的 视频链接地址。
然后进行比对,如果一致,则证明 视频连接地址 没有做 加密处理,
如果不一致 根据一定规则 将 接口返回的地址 处理成 真正需要的地址,
进而就可以 用代码进行数据获取了。
当本机的ip被目标服务器进行限制访问时,可以以肉机的形式使用一个正向代理进行访问。
搜索免费代理ip获取一个 正向代理的ip地址
proxies = {
"https":"https://11.22.33.44" # 代理ip 11.22.33.44,如果访问的是http开头的网址就把https改了
}
res = requests.get("https://www.baidu.com", proxies=proxies)
import re
# findall: 匹配字符串中所有符合正则的内容,返回列表
lst = re.findall(r"\d+", "我的手机号是123,我的电话是456") # lst ==> [123, 456]
# finditer: 匹配字符串中所有符合正则的内容,返回迭代器, 从迭代器中拿内容需要group方法
it = re.finditer(r"\d+", "我的手机号是123,我的电话是456")
for v in it:
print(v.group())
# search: 将 第一个匹配的结果 放到match对象中返回,拿数据需要用 group方法
s = re.search(r"\d+", "我的手机号是123,我的电话是456")
print(s.group()) # 123
# 预加载正则表达式: 如果一个正则被反复用到,可以将该正则直接 放进 一个变量中,提高正则编译效率
regexp_obj = re.compile(r"\d+")
it = regexp_obj.finditer("我的手机号是123,我的电话是456")
for v in it:
print(v.group())
# 单独提取正则的子查询内容
s = '''
tom
'''
obj = re.compile(r"(?P\w+) ", re.S) # re.S 的作用是让 . 能匹配换行符号
res = obj.search(s)
print(res.group()) # tom
print(res.group('wtt')) # 111
print(res.group('who')) # tom
import requests
from bs4 import BeautifulSoup
resp = requests.get(url)
# 解析数据
# 1、把页面源代码交给BeautifulSoup处理,生成bs对象
page = BeautifulSoup(resp.text, "html.parser") # 参数二 指定使用HTML解析器
# 2、从bs对象中查找数据
# find(标签, 属性) 返回第一个匹配
# 属性参数说明:
# 1可以单独写:find('div', class_="wtt") class 是python的关键字,为了区分后加下划线
# 2可以组合写:find("div", attr={"class": "wtt"})
div = page.find("div", attr={"class": "wtt"})
# find_all(标签, 属性) 返回所有的匹配
span = div.find_all("span")
text = span.text
sty = span.get("style")
import requests as r
from bs4 import BeautifulSoup
'''
step1:拿到主页面的源代码,然后提取到子页面的链接地址,href
step2:通过href拿到子页面的内容,从子页面中找到图片的下载地址,src
step3:下载图片
'''
# step1
url = "https://www.umeitu.com/bizhitupian/weimeibizhi/"
resp = r.get(url)
# 为了防止乱码,一定确保encoding的值 和 从获取网页的
# 标签中的charset一直,
resp.encoding = 'utf-8'
# 把网页源代码 交给bs
main_page = BeautifulSoup(resp.text, "html.parser")
alist = main_page.find("div", class_="TypeList").find_all("a") # 把范围第一次缩小
for a in alist:
# print(a.get('href')) # /bizhitupian/weimeibizhi/220638.htm
href = "https://www.umeitu.com" + a.get('href') # 拿到子页面的地址
# step2
child_page_resp = r.get(href)
child_page_resp.encoding = 'utf-8'
child_page = BeautifulSoup(child_page_resp.text, "html.parser")
p = child_page.find("p", align="center")
img = p.find("img")
src = img.get("src")
# print(src) # http://kr.shanghai-jiuxin.com/file/2020/1031/6b72c57a1423c866d2b9dc10d0473f27.jpg
# step3
img_resp = r.get(src)
# img_resp.content 这里拿到的是图片文件的字节串
img_name = src.split("/")[-1]
with open(img_name, mode="wb") as f:
f.write(img_resp.content)
break # 测试的 break
xpath是在XML文档中搜索内容的一门语言,html是XML的子集。
from lxml import etree
xml = '''
tom
cat
10.5
'''
tree = etree.XML(xml)
# 绝对查找
res = tree.xpath("/book") # / 表示层级关系,第一个/是根节点的意思
name_content = tree.xpath("/book/price/text()") # text() 拿文本
id_content = tree.xpath("/book/price/@id") # 获取id属性值
# 相对查找
book = tree.xpath("/book")
name_content = book.xpath("./name/text()")
# 查找子孙节点,类似于css选择器的空格
tree.xpath("/div//span") # div下的所有span标签
# 通过通配符查找指定代数的节点
tree.xpath("/div/*/*/span") # div下三代的所有span标签
tree.xpath("/div/name[1]/text()") # 通过索引 得到第一个name标签的文本内容
tree.xpath("/div/name[@alias='haha']/text()") # 通过属性 得到第二个name标签的文本内容
import requests as r
from lxml import etree
'''
step1:拿到主页面的源代码
step2:提取和解析数据
'''
# step1
url = "https://beijing.zbj.com/search/f/?type=new&kw=sass"
resp = r.get(url)
html = etree.HTML(resp.text)
# xpath参数的获取获取:
'''
F12-Elements-选定指定元素标签-右键-Copy-Copy XPath
'''
divs = html.xpath("/html/body/div[6]/div/div/div[2]/div[5]/div/div[1]")
for div in divs:
price = div.xpath("./div/div/a/div[2]/div[1]/span[1]/text()")
print(price) # ['¥1500']
title = div.xpath("./div/div/a/div[2]/div[2]/p/text()")
break
都知道python是一个带着脚镣跳舞的语言,所以我们这里就不说多进程or多线程了,
这里我们就利用python的协程,实现多请求的并发,
所以介绍一下aiohttp
(pip install aiohttp)包的基本使用
import aiohttp
import asyncio
url = "http://*****"
params = {'name': 'zhangsan', 'age': 10}
headers = {"User-Agent": "my-user-agent"}
cookies = {'cookies_name': 'test_cookies'}
async def wtt():
async with aiohttp.ClientSession() as session:
async with session.get(url, params=params, headers=headers) as response:
print(await response.text())
loop = asyncio.get_event_loop()
tasks = [wtt(), ]
loop.run_until_complete(asyncio.wait(tasks))
import aiohttp
import asyncio
url = 'http://httpbin.org'
payload = {'username': 'zhang', 'password': '123456'}
async def fetch():
async with aiohttp.ClientSession() as session:
async with session.post(url, data=payload) as response:
print(await response.text())
loop = asyncio.get_event_loop()
tasks = [fetch(), ]
loop.run_until_complete(asyncio.wait(tasks))
import aiohttp
import asyncio
url = 'http://localhost:8086'
payload = {'Tel': 'zhang', 'password': '123456'}
async def fetch():
async with aiohttp.ClientSession() as session:
async with session.post(url, json=payload) as response:
print(await response.text())
loop = asyncio.get_event_loop()
tasks = [fetch(), ]
loop.run_until_complete(asyncio.wait(tasks))
url = 'http://httpbin.org'
files = {'file': open('test.txt', 'rb')}
async def fetch():
async with aiohttp.ClientSession() as session:
async with session.post(url, data=files) as response:
print(await response.text())
loop = asyncio.get_event_loop()
tasks = [fetch(), ]
loop.run_until_complete(asyncio.wait(tasks))
import asyncio
import aiohttp
urls = [
"https://kr.zutuanla.com/file/2020/1031/191468637cab2f0206f7d1d9b175ac81.jpg",
"https://kr.zutuanla.com/file/2020/1031/774218be86d832f359637ab120eba52d.jpg",
]
async def img_download(url):
'''
1、发送异步http请求
2、得到http返回
3、保存文件
'''
# aiohttp.ClientSession() ==等同于== requests
img_name = url.split("/")[-1]
async with aiohttp.ClientSession() as r:
async with r.get(url) as resp:
with open(img_name, mode="wb") as f:
# 要用await 对异步代码的返回 进行等待
f.write(await resp.content.read())
print(img_name, "下载完成")
async def main():
tasks = []
for url in urls:
tasks.append(img_download(url))
await asyncio.wait(tasks)
if __name__ == '__main__':
asyncio.run(main())