爬虫

一、爬虫简介

什么是爬虫?

爬虫是一个应用程序
是指某一个用于爬取数据的应用程序
爬取的目标可以是整个互联网,也可以是单独的某个服务器
本质上相当于cs结构中的client客户端程序

爬虫的价值

互联网中最宝贵的就是数据了,例如淘宝的商品数据,链家的房源信息,拉钩的招聘信息等等,这些数据就像一座矿山,爬虫就像是挖矿的工具,掌握了爬虫技术,你就成了矿山老板,各网站都在为你免费提供数据。爬虫通过网路获取模板服务器的数据,来为自己创造价值

爬虫的流程

首先明确 我们的爬虫是客户端 要爬的数据在服务器上储存
所以需要借助网络编程,底层的网络协议已经有现成的封装不需要自己写
数据传输已经解决,为了使我们的数据对方可以看懂,对方的数据我们可以看懂,所以绝大多数网络传输采取HTTP协议
HTTP协议是超文本传输协议

  1.明确要爬取的url地址
  2.发送请求
    ①浏览器发送请求
    ②第三方requests模块
    ③内置urllib模块(不建议使用)
    ④selenium(自动化测试模块)用程序驱动浏览器发送请求
        之所以用selenium是因为一些网站需要人机交互(点击,拖拽等等操作)
    ⑤针移动app可以使用代理服务器,可以截获app端发送的请求信息 Charles(青花瓷)
    
 3.接收响应
    ①浏览器接受响应后会渲染页面进行展示 无法拿到数据 所以更多情况下使用浏览器来分析请求详情
    ②requests和urllib都会直接返回响应体
    ③selenium 提供了 find_element... 的接口用于获取数据
    
 4.解析数据
    ①re
    ②BeautifulSoup 封装了常用的正则表达式
    ③移动端返回的/ajax返回的json数据 直接json,load
 
 5.储存数据
    mysql等关系型数据库
    MongoDB redis 等非关系型数据库 一般用在高并发爬虫中

Scrapy 爬虫框架

爬虫处理反扒的措施?

user-agent
refer
token
cookies
使用代理ip

二、HTTP请求分析

请求的流程分析

请求地址
    浏览器发送的请求URL地址

请求方法
    get 参数跟在地址的后面
      post 参数放在body中

请求头
    请求头中的有用信息
    - user-agent 用户代理,用来识别客户端类型
    - refer   引用页面,用来识别用户从哪个页面过来的
    - Cookie 当页面需要验证用户身份时使用

请求体
    只在post请求时需要关注,通常post请求参数放在请求体中,例如登录时的用户名和密码
 

响应头
    location   当请求被重定向时 就会带有该字段  可以通过状态码30* 来识别重定向
    set-cookie:服务器返回的cookie信息,在访问一些隐私页面是需要带上cookie

响应体
    服务器返回数据,可能一下几种类型
    HTML格式静态页面 需要解析获取需要的数据
    json格式的结构化数据 直接纯粹的数据
    二进制数据(图片视频等) 通过文件操作直接写入文件

三、requests模块的使用

response属性

import requests
resp=requests.get('http://www.jianshu.com')
# respone属性
print(resp.text) # 获取解码后的内容 html

print(resp.content) # 获取二进制字节数据,下载视频直接将其写入,如果是页面的话放入html文件直接可以打开该页面

print(resp.json()) # 对响应进行json反序列化 聚合数据网站可以拿到返回值为json格式数据进行测试

print(resp.status_code) #获取状态 20* 请求成功  30* 重定向 40* 客户端请求错误 50* 服务器内部错误

print(resp.headers) #查看响应头

print(resp.cookies) #获取返回的cookies信息

print(resp.cookies.get_dict()) #将返回的cookies信息以字典形式返回

print(resp.url)  #请求地址

print(resp.request) #请求方式

print(resp.history) # 重定向历史

print(resp.encoding) # 响应体编码方式
print(resp.apparent_encoding) #文档编码方式
#当文档的编码方式与response解码方式不同时  需要手动指定
resp = requests.get("http://www.autohome.com/news")
# 以文档声明的方式来解码
resp.encoding = resp.apparent_encoding
print(resp.text)

print(resp.raw.read(100)) #以流的方式读取原始数据,没有经过HTTP协议解析的数据,必须明确stream=True

请求函数参数详解

#给服务器传参数
#get请求
params:给服务器传递参数,格式是一个字典 相当于将参数放在地址后面 requests.get(
'url',params={'key':'value'})
#post请求 data:post方法给服务器传递数据 data
={'name':'wade','pwd':'123'} json:post方法给服务器传递数据 json={'name':'wade','pwd':'123'}

在通过requests.post()进行POST请求时,传入报文的参数有两个,一个是data,一个是json。

data与json既可以是str类型也可以是dict类型

区别:

1.不管json是str还是dict,如果不指定headers中的content-type,默认为application/json

2.data为dict时,如果不指定content-type,默认为application/x-www-form-urlencoded,相当于不同form表单提交的形式

3.data为str时,如果不指定content-type,默认为application/json

4.application/json格式提取数据时只可以用request.body取得数据,application/x-www-form-urlencoded提取数据时既可以用request.body也可以用request.POST

 

headers

coookies

#超时时间 第一个参数为连接超时时间,第二个为响应超时时间
timeout  requests.post('http://www.baidu.com',timeout=(10,10))

allow_redirects : 是否允许重定向
    
proxies:使用代理服务器请求
'''
# 搜索免费http代理,写个爬虫扒http代理
import random
# 代理池  ********   对于爬虫而言是相当重要的一个参数
ps = ["121.228.240.101:9999","121.228.240.101:9999","121.228.240.101:9999"]
#使用代理服务器请求
resp = requests.post("http://news.baidu.com/?tn=news",proxies={"HTTP":random.choice(ps)})
with open("wade.html","wb") as f:
    f.write(resp.content)
    print(resp.text)
'''
    
files:上传文件
f = open(r"E:\python-li\爬虫\baidu.html","rb")
# 接收一个字典  key是服务器用于提取文件的字段名  f时要上传的文件对象
resp = requests.post("http://httpbin.org/post",files={"img":f})
print(resp.status_code)

爬取梨视频

进入梨视频页面
右键检查
点击Network 勾选Preserve log(保存日志,否则只显示每次响应的链接) 和 Disable cache(禁用缓存)
刷新页面
点击第一条请求 点击请求中的Headers 拿到Request URL 并取出其中的有用信息
            点击Response 我们可以看到解码后的内容与respone.text内容一样
 
爬取的流程
1.爬取首页中所有的视频的id
2.拼接详情地址并请求
3.通过re解析数据
4.将结果存储为json数据
'''
import requests
import re
import json
import os
from concurrent.futures import ThreadPoolExecutor

#要爬取的页数
page_num = int(input('请输入要爬取的页数:'))

#爬取的分类ID
category_id =int(input('请输入要爬取的分类ID:'))

#储存解析完成的数据
datas = []

#根目录
dir = os.path.dirname(__file__)

#请求首页列表
def get_page_data(categoryId):
    url = "https://www.pearvideo.com/category_loading.jsp?reqType=5&categoryId=%s&start="%categoryId

    for i in range(page_num):
        url1 = url + str(i*12)
        response = requests.get(url1)
        if response.status_code == 200:
            print('第%s页请求成功!'%(i+1))
            get_details(response)
            print('第%s获取完毕!'%(i+1))

#获取详情
def get_details(response):
    res = re.findall('<a href="(video_\d+)"',response.text)
    base_url = 'https://www.pearvideo.com/'
    for i in res:
        #拼接详情页面的地址
        detail_url = base_url + i
        detail_resp = requests.get(detail_url)

        #解析标题
        title = re.search('<h1 class="video-tt">(.*?)</h1>', detail_resp.text).group(1)

        #时间
        sub_date = re.search('<div class="date">(.*?)</div>', detail_resp.text).group(1)

        #点赞数
        f_count = re.search('<div class="fav" data-id="\d+">(\d+)</div>', detail_resp.text).group(1)

        #发布者姓名
        author = re.search('</i>(.*?)</div>', detail_resp.text).group(1)

        #详请
        content = re.search('<div class="summary">(.*?)</div>', detail_resp.text).group(1)

        #视频地址
        video_url = re.search('srcUrl="(.*?)"',detail_resp.text).group(1)

        #获取的数据字典
        dic = {'title':title,'sub_date':sub_date,'f_count':f_count,'author':author,'content':content,'video_url':video_url}

        #开始下载视频文件
        #异步提交任务到线程池(IO密集型)
        download_video(video_url,title)
        pool.submit(download_video,video_url,title)

        datas.append(dic)

#下载视频
def download_video(video_url,video_name):
    try:
        print('开始下载',video_name)
        response = requests.get(video_url)
        video_name = video_name.replace('"','')
        video_name = video_name.replace('?','')
        file_path = os.path.join(dir,'videos',video_name+'.mp4') #必须存在videos文件夹

        if os.path.exists(file_path):
            print(video_name,'已经下载过了!')
            return

        with open(file_path,'wb') as f:
            f.write(response.content)
            print('下载完成!')
    except Exception as e:
        print('任务失败,%s'%e)

#将数据写入json文件
def write_json():
    with open('datas.josn','wt') as f:
        json.dump(datas,f)

if __name__ == '__main__':
    #开启线程
    pool = ThreadPoolExecutor()

    #获取详请
    get_page_data(category_id)

    #写入
    write_json()
'''

百度搜索

import requests

key = input('请输入关键字:')

#百度搜索url https://www.baidu.com/s?wd=%E9%9F%A6%E5%BE%B7
url = 'https://www.baidu.com/s?'

#Request Headers明确User-Agent
user_agent = 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36'

response = requests.get(url,
                        #get请求 参数放在地址后或是使用params参数来指定 一个字典
                        params={'wd':key},
                        #请求头中携带
                        headers={'User-Agent':user_agent})
with open('baidu.html','wb') as f:
    f.write(response.content)

github带有cookies的请求

import requests
url = "https://github.com/"
user_agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36"

resp = requests.get(url,
            #登陆成功后,从Request Headers 中获取User-Agent 和 Cookie
             headers = {"user-agent":user_agent,
                        "Cookie":"_ga=GA1.2.239001333.1554254017; _octo=GH1.1.152181730.1554254040; has_recent_activity=1; tz=Asia%2FShanghai; _device_id=6796ba7f23149d5bfaa3e56c429ff9e4; user_session=b99nc_Q_zIicwhtSgNiU0NlSzEoo6QSzaJ8YNThH1XEXbGwp; __Host-user_session_same_site=b99nc_Q_zIicwhtSgNiU0NlSzEoo6QSzaJ8YNThH1XEXbGwp; logged_in=yes; dotcom_user=lizeqian828; _gh_sess=UUY1OHlmK3BVR1FVZGk2OWlmN21KOUN3MUErYS83KzVjY0hGb1UwMCtJYldNbklTNVlHVFhPZ2NoMUxzRUhTM29vV0czV1djRUdMcjRRZEJzUzRYdW92M2xRVW9CY1NOak5wbGtBVFhFV1dxZmpmeVJld3JudEQvVFdON2RTTXNkM1pDcjlDSk5DNXZEYnNHUmovenE1d2FVZzlRSHdLeE9rWEk2TDU0ek4yRWpzeGNsd01OY2ZQZ0g3QmhYT1JwVzRvbDRJdXZFVnpGUzhxNCtLcUtQM0FQRDlWSmtENmwwdW96SEJTTkpHTmRWNFFoT1lBYzQvd1BPM21EOVVqRUZORUQwbHFxbitGZHFVaFJmVGNVK1E9PS0tZFJSdk5CVFRRcEp5RndwQzIranNpUT09--8df770bf594457931f66844d2f994068d7c8b587"},
             )


#状态码
print(resp.status_code)
#判断是否登陆成功 True成功
print("Your most active repositories will appear here" in resp.text)

模拟github登录

post请求 参数使用data参数来指定 是一个字典
如果是登录请求一般都需要带token
    先发送请求获取token的值 token值一定在一个form表单中 存在于页面数据及响应体中
"""
1.获取token
地址:https://github.com/login
token放在了响应体中
"""
import requests,re

# 1.获取token
#获取登录界面的地址get请求
login_page_url = "https://github.com/login"
resp = requests.get(login_page_url) # 请求首页
token = re.search('authenticity_token" value="(.*?)" /> ',resp.text).group(1)

# 获取返回的cookie,进入登录页面会产生一个cookie,必须获取否则不能提交post请求
cookie = resp.cookies.get_dict()

# 2.请求登录接口
#提交完成登录的地址post请求
login_url = "https://github.com/session"
resp2 = requests.post(login_url,
              headers={
                  "Referer": "https://github.com/login",
                  "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36",
              },
              #post请求的数据以Form Data形式发给服务器
              data={"commit": "Sign in",
                    "utf8": "",
                    "authenticity_token": token,
                    "login": "lizeqian828",
                    "password": "lizeqian0828",
                    "webauthn-support": "supported"},
              cookies = cookie)

#状态码
print(resp2.status_code)

with open("github_home.html","wb") as f:
    f.write(resp2.content)

#判断是否登录成功
print("Your most active repositories will appear here" in resp2.text)

session发送请求

"""
session 可以自动获取cookie并传递
"""
import requests, re

session = requests.session()

# 1.获取token
# 获取登录界面的地址get请求
login_page_url = "https://github.com/login"
resp = session.get(login_page_url)  # 请求首页
token = re.search('authenticity_token" value="(.*?)" /> ', resp.text).group(1)

# 2.请求登录接口
# 提交完成登录的地址post请求
login_url = "https://github.com/session"
resp2 = session.post(login_url,
                      headers={
                          "Referer": "https://github.com/login",
                          "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36",
                      },
                      # post请求的数据以Form Data形式发给服务器
                      data={"commit": "Sign in",
                            "utf8": "",
                            "authenticity_token": token,
                            "login": "lizeqian828",
                            "password": "lizeqian0828",
                            "webauthn-support": "supported"},
                     #默认为True登陆成功完成跳转,False不完成跳转
                      allow_redirects=False)

# 状态码
print(resp2.status_code)

with open("github_home.html", "wb") as f:
    f.write(resp2.content)

# 判断是否登录成功
print("Your most active repositories will appear here" in resp2.text)

 

posted @ 2019-04-22 16:52  Zhuang_Z  阅读(494)  评论(0编辑  收藏  举报