爬虫——网页蜘蛛

简介

网络爬虫(又被称为网页蜘蛛、网络机器人),是一种按照一定的规则,自动抓取互联网信息的程序或者脚本。

Robots协议

直译为机器人排除协议,又可称为爬虫协议、机器人协议,是指网站所有者通过一个置于网站根目录下的文本文件,即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模块篇

Requests模块是第三方模块,需要pip安装,requests模块在python内置模块的基础上进行了高度的封装,
从而使得python进行网络请求时,变得更加简洁和人性化。

GET 请求

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之前。
'''

POST 请求

HTTP 协议规定 POST 提交的数据必须放在 请求体 中,但协议并没有规定数据必须使用什么编码方式,
服务端通过是根据请求头中的Content-Type字段来获知请求中的消息主体是用何种方式进行编码,
再对消息主体进行解析。具体的编码方式包括:

  1. 最常见post提交数据的方式,以form表单形式提交数据
payload = {
    'key1': 'value1',
    'key2': 'value2'
}

res = requests.post("http://httpbin.org/post", data=payload)
print(res.text)

# 关闭请求
res.close()
'''
  "form": {
    "key1": "value1", 
    "key2": "value2"
  },
  
'''
  1. 以json形式发送post请求
payload = {
    'key1': 'value1',
    'key2': 'value2'
}

res = requests.post("http://httpbin.org/post", data=json.dumps(payload))
print(res.text)

# 关闭请求
res.close()
  1. 以multipart形式发送post请求

post 发送 文件

with open('666.jpg', 'rb') as file:
    files = {'filess': file}
    r = R.post(gugulocal, files=files)
    print(r.text)

cookie 处理

  • 姿势一

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)

数据解析篇

re模块

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

bs4模块

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模块

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)包的基本使用

GET 请求

  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))

POST 请求

  1. 最常见post提交数据的方式,以form表单形式提交数据
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))
  1. 以json形式发送post请求
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))

  1. 发送文件
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())

selenium篇

你可能感兴趣的:(爬虫,python,http)