[数据采集笔记06]——01动态内容的抓取与Ajax

1. JavaScript与动态内容

对于同一个url,有时用户抓取的内容与在浏览器中看到的内容不同, 这是因为用户通过程序获取的响应内容都是原始的html数据, 而浏览器中所看到的数据是在html的基础上经过JavaScript进一步处理加工后生成的效果。


比如,简书主页(如下图片)。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0tpwh3Y8-1590159645564)(D:\笔记图片集\1590156229397.png)]

当你点击阅读更多时, 页面会加载出新的文章信息。 可是我们查看网页源码中并没有包含这些信息。(篇幅原因, 读者请自行在页面中点击右键查看网页源代码)。这是因为这些页面中用到AJAX技术(Asunchronous JavaScript and XML, 异步JavaScript与XML)。


又比如, 拉勾网中切换页面。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kGaec8uX-1590159645567)(D:\笔记图片集\1590156890709.png)]

对于之前要访问下一个页面的信息是,需要请求新的url (现在我们也会看到有些网页url中的有一个类似page=页数的请求参数)。然后获取响应, 重新加载整个页面。而使用Ajax技术来说,不需要对整个页面进行刷新, 只要加载其中一部分数据就可以了(拉勾网就是用到这种技术)。

1.1.AJAX

其中某一本书上说:与其说Ajax是一种“技术”, 不如说是一种“方案”。

我们知道javascript是一种很流行的前端脚本语言。使用javascript调度加载局部信息, 可以看成ajax技术。尴尬的是我们的爬虫不会执行这些javascript代码。


对于使用ajax这种技术的页面,我们对其的爬取有两种方法:

  • 逆向工程

    通过分析AJAX内容, 观察请求目标、请求内容和请求参数等信息,编写出模拟这样的Javascript请求,最终获取页面信息。

  • 直接模拟浏览器环境, 这里用到selenium库, 一个自动化库, 可以模拟浏览器。


网页中的AJAX过程一般简单的可以理解为: 发送请求——获得数据——显示元素的流程。

其中, 在第一步“发送请求”时,客户端主要借助了一个所谓的XMLHttpRequest对象(xhr)。我们使用python发送请求代码如下:

import requests
resp = requests.get(url)

浏览器使用XMLHttpRequest也类似于此。 不过浏览器用的JavaScript语言。通过AJAX技术, 浏览器最终会在XMLHttpRequest的responseText属性中获得相应内容, 常见的响应内容有json、html等。

XMLHttpRequest是一个API,他可以微客户端提供在客户端和服务端之间传输数据的功能。它提供了一个通过URL来获取数据的简单方式, 并且不会使整个页面刷新。 具体XMLHttpRequest的定义可以参考Mozilla给出的说明。

1.1.1.通过逆向工程法爬取携程某酒店常见问答板块的实例

代码1

import requests
import json
"""
爬取携程酒店的常见问题
该页面使用ajax技术
 
使用 request对其进行简单的爬取
"""


#生成索要爬取的url
urls = ('https://hotels.ctrip.com/Domestic/tool/AjaxHotelFaqLoad.aspx?hotelid=429548&currentPage={}'.format(i) for i in range(1, 3))


def get_content(url):
    """
    获取响应内容的函数
    :param url:
    :return:
    """
    #传入url 获取响应
    resp = requests.post(url)
    #解析json字符串
    js= json.loads(resp.text)
    # print(js)
    # raise Exception('暂停')
    return js

def parser_content(js):
    askList = js['AskList']
    return askList

def spider():
    """
    爬取调度函数
    :return:
    """
    global urls

    for url in urls:
        js = get_content(url)
        askList = parser_content(js)
        for i in askList:
            print('-问:{0}\n     答{1}'.format(i['AskContentTitle'], i['ReplyList']))

if __name__ == '__main__':
    spider()

代码2

相比代码1, 代码2是更好的选择。

import requests
import time
from pymongo import MongoClient

client=MongoClient()

#使用名为ctrip的数据库
db = client['ctrip']

#使用其中的collection表:hotelfaq(酒店常见问题)
collection=db['hotelfaq']

global hotel
global max_page_num

raw_url='https://hotels.ctrip.com/Domestic/tool/AjaxHotelFaqLoad.aspx'

#headers
headers = {
    'host':'hotels.ctrip.com',
    'referer': 'https://hotels.ctrip.com/hotel/429548.html?masterhotelid=429548&hcityid=1',
    'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36'
}

def get_json(hotel, page):
    """
    获取json信息
    :param hotel: 酒店的id
    :param page: 页码
    :return:
    """
    params = {
        'hotelid':hotel,
        'page':page
    }

    try:
        resp = requests.get(raw_url,headers=headers, params=params)
        if resp.ok: #成功访问
            print(type(resp.json()), type(resp.text), sep='\n')
            return resp.json() #返回json数据

    except Exception as e:
        print('Error here:\t', e)

def json_parser(json):
    """
    解析json
    :param json:
    :return:
    """
    if json is not None:
        asks_list=json.get('AskList')

        if not asks_list:
            return None
        for ask_item in asks_list:
            one_ask = {}
            one_ask['id'] = ask_item.get('AskId')
            one_ask['hotel'] = hotel
            one_ask['createtime'] = ask_item.get('CreateTime')
            one_ask['ask'] = ask_item.get('AskContentTitle')
            one_ask['reply'] = []

            if ask_item.get('ReplyList'):
                for reply_item in ask_item.get('ReplyList'):
                    one_ask['reply'].append(
                        (reply_item.get('ReplierText'),reply_item.get('ReplyContentTitle'), reply_item.get('ReplyTime'))
                    )
                yield one_ask #使用生成yeild方法

def save_to_mongo(data):
    """
    存到数据库
    :param data:
    :return:
    """
    if collection.insert_one(data): #插入一条数据
        print('Saving to db!')

def worker(hotel):
    max_page_num = int(input('input max page num'))#输入最大页数

    for page in range(1, max_page_num+1):
        time.sleep(1.5)
        print('page now \t{}'.format(page))

        raw_json=get_json(hotel,page) #获取原始json数据
        res_set=json_parser(raw_json)
        for res in res_set:
            print(res)
            save_to_mongo(res)

if __name__ == '__main__':
    hotel = int(input('input hotel id.'))
    worker(hotel)

参考

  • 吕云翔 张扬 Python网络爬虫实战
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值