• python3编写网络爬虫13-Ajax数据爬取


    一、Ajax数据爬取

    1. 简介:Ajax 全称Asynchronous JavaScript and XML 异步的Javascript和XML。
    它不是一门编程语言,而是利用JavaScript在保证页面不被刷新,页面链接不改变的情况下与服务器交换数据,
    获得数据后,再利用JavaScript改变页面。

    示例:新浪微博 热门

    2. 基本原理

    2.1 发送请求

    JavaScript可以实现页面交互功能 Ajax也不例外 它是由JavaScript实现的,实际上执行了如下代码

    var xmlhttp;
    if(window.XMLHttpRequest){
      # code for IE7+,Firefox,Chrome,Opera,Safari
      xmlhttp = new XMLHttpRequest();#新建对象
    }else{#code for IE6,IE5
      xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
    }
    
    xmlhttp.onreadystatechange = function(){#设置监听
      if(xmlhttp.readyState==4 && xmlhttp.status==200){
        document.getElementById("myDiv").innerHTML = xmlhttp.responseText;
      }
    }
    xmlhttp.open("POST","/ajax/",true);
    xmlhttp.send();#发送请求

    这是javascript对ajax最底层的实现,新建XMLHttpRequest对象 调用onreadystatechange属性设置监听
    调用open和send方法发送请求。

    之前用python发送请求可以得到响应结果 但这里的请求发送变成了javascript来完成,
    由于设置了监听 服务器响应结果时,onreadystatechange属性会被触发 然后解析里面的内容。

    2.2 解析内容

    得到响应内容后,onreadystatechange属性对应的方法便会触发,利用xmlhttp的responseText属性接收。
    类似python中利用requests向服务器发送请求 然后得到响应结果的过程。
    返回的结果可能是HTML 也可能是JSON 只需要在js中做进一步处理 例如返回json 可以进行解析和转化。

    2.3 渲染页面

    js有改变网页内容的能力,解析完响应内容后 调用js里面document.getElementById().innerHTML 改变某个元素内的源代码
    这样网页的内容就改变了 简称DOM操作

    2.4 总结 3个步骤都是js完成的 微博下拉实际上就是js向服务器发送一个Ajax请求 然后获取服务器响应内容
    解析并渲染到网页中。真实数据都是js一次次ajax请求得到的。
    如果需要抓取数据 就要知道 请求怎么发送的? 发送到哪里?发送了哪些参数?


    3. Ajax分析方法

    3.1 查看方法

    测试地址:

    https://m.weibo.cn/u/1195242865


    浏览器开发者工具 ajax请求类型为xhr
    Request Headers信息为 X-Requested-With: XMLHttpRequest 标记此请求为Ajax请求

    3.2 过滤请求

    点击开发者工具Network -> XHR 过滤为ajax请求 不断滑动页面 出现新的请求


    发送请求地址:

    https://m.weibo.cn/api/container/getIndex?type=uid&value=1195242865&containerid=1076031195242865&since_id=4311085060664535

    参数:
    type: uid
    value: 1195242865
    containerid: 1076031195242865
    page:1

    4. 提取结果

    from urllib.parse import urlencode
    import requests
    from pyquery import PyQuery as pq
    
    
    base_url = 'https://m.weibo.cn/api/container/getIndex?'#请求url的前半部分
    
    headers = {
      'Host':'m.weibo.cn',
      'Referer': 'https://m.weibo.cn/u/1195242865',
      'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.80 Safari/537.36',
      'X-Requested-With': 'XMLHttpRequest',
    }
    
    # 构造url 发送请求
    def get_page(page):
      params = {
        'type': 'uid',
        'value': '1195242865',
        'containerid': '1076031195242865',
        'page':page,
      }
      url = base_url + urlencode(params)
    
      try:
        response = requests.get(url,headers=headers)
        if response.status_code == 200:
          return response.json()
      except requests.ConnectionError as e:
        print('Error',e.args)
    
    #解析方法 提取id 正文 赞数 评论数 转发数 遍历cards 获取mblog中的各个信息
    
    def parse_page(json):
      if json:
        items = json.get('data').get('cards')
        for item in items:
        # print(item)
        item = item.get('mblog')
        if item:
          weibo = {} #定义空字典接收数据
          weibo['id'] = item.get('id')
          weibo['text'] = pq(item.get('text')).text()
          weibo['attitudes'] = item.get('attitudes_count')
          weibo['comments'] = item.get('comments_count')
          weibo['reposts'] = item.get('reposts_count')
          yield weibo
    
    #遍历page 一共10页 将提取到的结果打印输出
    
    if __name__ == '__main__':
      for page in range(1,3):
        json = get_page(page)
        results = parse_page(json)
        for result in results:
          print(result)

    5. 添加到mongodb数据库中

    from pymongo import MongoClient
    
    client = MongoClient()
    db = client['weibo']
    collection = db['weibo']
    
    def save_to_mongo(result):
      if collection.insert(result):
        print('Saved to Mongo')

    查看mongo内容

    启动mongo服务 查看库 show dbs; 查看当前在哪个库 db.getName(); 进入库 use dbname;
    查看数据 db.dbname.find(); 删除当前所在库 db.dropDatabase();


    至此分析模拟Ajax请求爬取微博列表完成 爬取结果不重要 还有好多地方可以完善 比如动态计算页码,查看微博全文等等。
    主要是让大家了解抓取原理

    实例:爬取今日头条街拍图片

    地址:http://www.toutiao.com 关键字 :街拍

    offset: 0
    format: json
    keyword: 街拍
    autoload: true
    count: 20
    cur_tab: 1
    from: search_tab
    pd: synthesis

    基本代码

    import requests
    import json
    import time
    import re
    import os
    from random import choice
    from hashlib import md5
    
    url = "https://www.toutiao.com/search_content/?"
    header = {
      'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.80 Safari/537.36',
    }
    keyword = '街拍'
    
    has_gallery_lists = []
    no_gallery_lists = []
    
    
    def SearchPageParser(offset=0):
      payload = {
        'offset': offset,
        'format': 'json',
        'keyword': keyword,
        'autoload': 'true',
        'count': 30,
        'cur_tab': 1,
        'from': 'search_tab'
      }
    
      count = 0
    
      try:
        response = requests.get(url, headers=header, params=payload)
        content = None
        #打印拼接请求后的url
        # print("Parser " + response.url)
        if response.status_code == requests.codes.ok:
          content = response.text
          data = json.loads(content)
    
          if not data:
            return
    
          for article in data.get('data'):
            if True == article.get('has_gallery') and True == article.get('has_image'):
              has_gallery_lists.append(article.get('article_url'))
              count += 1
    
            if False == article.get('has_gallery') and True == article.get('has_image'):
              no_gallery_lists.append(article.get('article_url'))
              count += 1
    
            return count
    
       except Exception as e:
        print(e)
        return
    
    #保存本地函数
    def SaveImage(imageURL,title):
    #判断文件夹是否存在
      if not os.path.exists(title):
        os.mkdir(title)
      try:
        response = requests.get(imageURL)
        if response.status_code == 200:
          file_path = '{0}/{1}.{2}'.format(title, md5(response.content).hexdigest(), 'jpg')
          # 判断是否重名
          if not os.path.exists(file_path):
            with open(file_path,'wb') as f:
            f.write(response.content)
          else:
            print('Already Downloaded',file_path)
      except:
        print('Failed to Save Image')
    
    #第一种页面
    def HasGalleryParser():
      if 0 == len(has_gallery_lists):
        return
    
      # 正则
      pattern = re.compile('gallery: JSON.parse("(.*?)max_img', re.S)
      pattern_t = re.compile('<title>(.*?)</title>', re.S)
    
      while has_gallery_lists:
        this = has_gallery_lists.pop()
    
      try:
        response = requests.get(this, headers=header)
        content = None
    
        if response.status_code == requests.codes.ok:
          content = response.text
          data = pattern.findall(content)
          pattern_t.findall(content)
    
          if data:
          #去掉多余符号
            data = data[0][:-4].replace('\', '') + ']}'
            img_urls = json.loads(data).get('sub_images')
            title = "".join(pattern_t.findall(content))
            for img_url in img_urls:
            #保存函数
              SaveImage(img_url.get('url'),title)
          else:
            print("BadPageURL[GalleryParser, {0:s}]".format(this))
    
      except Exception as e:
        print(e)
        return
    
      time.sleep(0.25)
    
    #第二种页面
    def NoGalleryParser():
      if 0 == len(no_gallery_lists):
        return
    
      while no_gallery_lists:
        this = no_gallery_lists.pop()
        #正则匹配
        pattern = re.compile('&lt;img src&#x3D;&quot;(.*?)&quot;', re.S)
        pattern_t = re.compile('<title>(.*?)</title>',re.S)
        try:
          response = requests.get(this, headers=header)
          content = None
    
          if response.status_code == requests.codes.ok:
            content = response.text
            img_urls = pattern.findall(content)
            img_title = "".join(pattern_t.findall(content))
            if img_urls:
              for img_url in img_urls:
                #保存函数
                SaveImage(img_url,img_title)
            else:
              # 过滤地址
              print("BadPageURL[NoGalleryParser, {0:s}]".format(this))
    
        except Exception as e:
          print(e)
          return
    
      time.sleep(0.25)
    
    
    if __name__ == "__main__":
      #计数变量
      x, count = 0, 0
      # 获取头条页面
      cnt_urls = SearchPageParser(x)
    
      while count < 20 and cnt_urls:
        cnt_urls = SearchPageParser(x + 20)
        count += cnt_urls
        x += 20
        time.sleep(0.55)
        #打印分页地址
        # print("Get {0:d} URL(s) in total.".format(count))
      # 分析页面
      HasGalleryParser()
      NoGalleryParser()
  • 相关阅读:
    echarts x轴文字显示不全(xAxis文字倾斜比较全面的3种做法值得推荐)
    从输入URL到页面加载的过程?由一道题完善自己的前端知识体系!
    浏览器多进程架构、浏览器内核多线程、js单线程、GUI 渲染线程 与 JavaScript引擎线程互斥 原理
    通过script src引入ElementUI时,使用语句:window.ELEMENT.MessageBox.alert(xxx) 调用弹出框
    计算2个日期之间的天数
    js 字符串转对象
    requestAnimationFrame 知识点
    vue项目权限控制
    css中权重与继承
    Flex Basis与Width的区别
  • 原文地址:https://www.cnblogs.com/liuxiaosong/p/10363894.html
Copyright © 2020-2023  润新知