zoukankan      html  css  js  c++  java
  • 爬虫

    一、爬虫简介

    什么是爬虫?

    爬虫是一个应用程序
    是指某一个用于爬取数据的应用程序
    爬取的目标可以是整个互联网,也可以是单独的某个服务器
    本质上相当于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爬虫aidu.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)
  • 相关阅读:
    nio原理分析与代码实现
    SpringMvc下载excel文件
    centos6下mysql-5.5.21的安装
    CentOS下开启mysql远程连接,远程管理数据库
    客户端更新策略
    IDEA插件开发基础
    简易ORM(基于注解)
    尝试使用Java6API读取java代码
    Java源代码分析与生成
    Common Configration实验
  • 原文地址:https://www.cnblogs.com/lizeqian1994/p/10751111.html
Copyright © 2011-2022 走看看