大家好,欢迎来到IT知识分享网。
一、简介
有时候我们在用 requests 抓取页面的时候,得到的结果可能和在浏览器中看到的不一样:在浏览器中可以看到正常显示的页面数据,但是使用 requests 得到的结果并没有。这是因为 requests 获取的都是原始的 HTML 文档,而浏览器中的页面则是经过 JavaScript 处理数据后生成的结果,这些数据的来源有多种,可能是通过 Ajax 加载的,可能是包含在 HTML 文档中的,也可能是经过 JavaScript 和特定算法计算后生成的。
对于第一种情况,数据加载是一种异步加载方式,原始的页面最初不会包含某些数据,原始页面加载完后,会再向服务器请求某个接口获取数据,然后数据才被处理从而呈现到网页上,这其实就是发送了一个 Ajax 请求。
照 Web 发展的趋势来看,这种形式的页面越来越多。网页的原始 HTML 文档不会包含任何数据,数据都是通过 Ajax 统一加载后再呈现出来的,这样在 Web 开发上可以做到前后端分离,而且降低服务器直接渲染页面带来的压力。
所以如果遇到这样的页面,直接利用 requests 等库来抓取原始页面,是无法获取到有效数据的,这时需要分析网页后台向接口发送的 Ajax 请求,如果可以用 requests 来模拟 Ajax 请求,那么就可以成功抓取了。
1. 什么是Ajax
Ajax,全称为 Asynchronous JavaScript and XML,即异步的 JavaScript 和 XML。它不是一门编程语言,而是利用 JavaScript 在保证页面不被刷新、页面链接不改变的情况下与服务器交换数据并更新部分网页的技术。
对于传统的网页,如果想更新其内容,那么必须要刷新整个页面,但有了 Ajax,便可以在页面不被全部刷新的情况下更新其内容。在这个过程中,页面实际上是在后台与服务器进行了数据交互,获取到数据之后,再利用 JavaScript 改变网页,这样网页内容就会更新了。
2. 手写Ajax接口服务
- 框架简介
Flask是一个基于Python并且依赖于Jinja2模板引擎和Werkzeug WSGI 服务的一个微型框架 WSGI :Web Server Gateway Interface(WEB服务网关接口),定义了使用python编写的web app与web server之间接口格式
- 环境搭建
pip install flask
3. 前端程序设计
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>Title</title></head><style> td{ width: 65px; text-align: center; font-size: 18px; }</style><body><h1>hello</h1><!--style 写 CSSHTML语法 标签 table 表格--><table style="margin: 0 auto; border: 1px solid; margin-top: 100px"> <tr> <th>序号</th> <th>姓名</th> <th>年龄</th> </tr> <tbody> {% for i in data.data %} <tr> <td>{{ i.id }}</td> <td>{{ i.name }}</td> <td>{{ i.age }}</td> </tr> {% endfor %} </tbody></table></body></html>
4.后端接口设计(Flask)
from flask import Flask,render_template,jsonify# 实例app = Flask(__name__)@app.route('/')def ff(): return render_template('feifei.html')@app.route('/xl')def susu(): data = [] for i in range(1, 10): data.append( {'id': i, 'name': '鱼' + str(i), 'age': 18 + i} ) # 构造数据格式 content = { 'status': 0, 'data': data } return jsonify(data=content)@app.route('/api')def index(): data = [] for i in range(1,7): data.append( {'id':i, 'name': '鱼'+str(i), 'age':18+i} ) # 构造数据格式 content = { 'status': 0, 'data':data } print(content) # flask 框架 返回数据给前端 return render_template('index.html',data=content)if __name__ == '__main__': app.run()
二、基本原理
初步了解了 Ajax 之后,我们再来详细了解它的基本原理。发送 Ajax 请求到网页更新的这个过程可以简单分为以下 3 步:
- 发送请求
- 解析内容
- 渲染网页
三、数据采集案例
1. Ajax 数据加载
Ajax 其实有其特殊的请求类型,其 Type 为 xhr,这就是一个 Ajax 请求。用鼠标点击这个请求,可以查看这个请求的详细信息。
示例网站:
https://danjuanapp.com/rank/performance http://www.cninfo.com.cn/new/commonUrl?url=disclosure/list/notice#szseGem https://cs.lianjia.com/
2. 数据采集实战
采集地址:https://cs.lianjia.com/
# encoding: utf-8 import requests from loguru import logger import json class Spdier_data(): def __init__(self): self.headers = { "Accept": "application/json, text/javascript, */*; q=0.01", "Accept-Language": "zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7,zh-TW;q=0.6", "Cache-Control": "no-cache", "Connection": "keep-alive", "Pragma": "no-cache", "Referer": "https://cs.fang.lianjia.com/loupan/pg2/", "Sec-Fetch-Dest": "empty", "Sec-Fetch-Mode": "cors", "Sec-Fetch-Site": "same-origin", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.67 Safari/537.36", "X-Requested-With": "XMLHttpRequest", "sec-ch-ua": "^\\^", "sec-ch-ua-mobile": "?0", "sec-ch-ua-platform": "^\\^Windows^^" } def http(self,url,params): res = requests.get(url,params=params,headers=self.headers) if res.status_code == 200: return res def get_data(self,url,params): response = self.http(url=url,params=params) items = response.json() lists = items.get('data').get('list') for i in lists: item = {} item['address'] = i.get('address') item['city_name'] = i.get('city_name') item['decoration'] = i.get('decoration') item['district'] = i.get('district') item['title'] = i.get('title') item['show_price_info'] = i.get('show_price_info') logger.info(json.dumps(item)) self.save_data(item) def save_data(self,data): with open('data.json','a',encoding='utf-8') as f: json.dump(data, f, ensure_ascii=False, indent=4) f.write(',') def run(self): for i in range(1,3): url = "https://cs.fang.lianjia.com/loupan/pg{}/".format(str(i)) params = { "_t": "1" } self.get_data(url,params) if __name__ == '__main__': Spdier_data().run()
四、数据去重算法
数据去重算法使用redis进行承载
redis 去重 集合类型,还有布隆算法。都是数据去重常用技术。
# encoding: utf-8
import requests
from base_request import Spiders
from lxml import etree
from loguru import logger
import time
import redis
client = redis.Redis()
import hashlib
class Crawl(Spiders):
def __init__(self):
self.url = 'https://36kr.com/information/web_news/latest/'
self.maps = lambda x:x[0] if x else x
def ma5_data(self,content):
m = hashlib.md5()
m.update(content.encode())
return m.hexdigest()
def crawl(self):
res = self.fetch(self.url)
html = etree.HTML(res.text)
obj = html.xpath('//div[@class="information-flow-list"]/div')
for i in obj:
title = self.maps(i.xpath('.//p[@class="title-wrapper ellipsis-2"]/a/text()'))
data_z = self.ma5_data(title) # 对数据进行压缩 减少内存损耗
tag = client.sadd('36k',data_z) # 返回值 0 1
if tag:
# 表示数据可以继续爬 对数据进行入库 logger = print
logger.info('可以入库{}'.format(title))
else:
time.sleep(5)
logger.info('休息5秒钟')
self.crawl()
def save(self,data):
with open('data.txt','a',encoding='utf-8') as x:
x.write(data)
x.write('\r\n')
def run(self):
while True:
logger.info('开始启动爬虫')
self.crawl()
if __name__ == '__main__':
Crawl().run()
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://yundeesoft.com/59916.html