首页 > 编程语言 > 详细

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

时间:2019-02-12 10:40:12      阅读:231      评论:0      收藏:0      [点我收藏+]

一、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()

 

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

原文:https://www.cnblogs.com/liuxiaosong/p/10363894.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!