详细数据:数据采集实战:动态网页数据采集

优采云 发布时间: 2022-10-07 04:15

  详细数据:数据采集实战:动态网页数据采集

  Part1简介

  在上一条推文中,我们已经解释了静态网页的 采集 方法。在本文中,我们介绍动态网页的方法采集。

  本文采集的例子网站为:我们的目标是采集网页中指定的文字信息,并保存。

  完整代码见文末附件!

  Part2 什么是动态网页

  通常,我们要提取的数据不在我们下载的 HTML 源代码中。比如我们刷QQ空间或者微博评论的时候,一直往下滑,网页不刷新就会越来越长,内容越来越多。

  具体来说,当我们浏览网站时,用户的实际操作(如向下滚动鼠标滚轮加载内容)不断向服务器发起请求,并使用JavaScript技术将返回的数据转换成新的内容添加到网页。以百度图片为例:我们输入百度图片后,搜索我们想找的图片,然后不断向下滚动页面。我们会看到网页中不断加载图片,但是网页没有刷新。这个动态加载页面。

  Part3 手册采集操作步骤

  本文中采集的例子网站为: 内容如下图所示:

  假设我们需要采集的内容是:文章的标题,关键词,这4部分的发布日期和详情链接,对于标题的3部分,关键词,发布日期信息我们可以在列表页面上看到。详情链接,我们还需要点击网站到采集上的指定详情页面,如下图:

  

  假设我们要采集有很多内容,单独手动采集操作会浪费很多时间,那么我们可以使用Python来自动化采集数据。

  Part4 自动采集的步骤(一)动态加载页面分析

  在不刷新网页的情况下,网站需要点击网页末尾的按钮来加载新数据,如下图所示:

  我们打开开发者工具(谷歌浏览器按F12),点击过滤器XHR,然后多次点击网页底部的按钮加载内容。我们可以看到,每次点击按钮,我们都可以抓包,我们查看抓包信息,可以发现请求返回的响应内容中收录了我们想要的数据。实际操作如下:

  网页中显示的内容:

  所以我们可以直接请求这个接口来获取我们想要的数据。我们首先提取这三个不同请求的URL,如下图:

  第2页:https://www.xfz.cn/api/website/articles/?p=2&n=20&type=<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />第3页:https://www.xfz.cn/api/website/articles/?p=3&n=20&type=<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />第4页:https://www.xfz.cn/api/website/articles/?p=4&n=20&type=

  提示:此 URL 是带有参数的 GET 请求。域名和参数用?分隔,每个参数用&amp;分隔。

  我们观察每个页面的 URL 参数的变化,发现 p 是三个参数中的一个可变参数。我们每点击一次,p就加1,所以p参数和翻页有关。我们可以通过修改 p 参数来访问它。从不同页面的信息内容我们也可以推断,当p参数的值为1时,就是请求网站的第一页的内容。

  (二)代码实现 1.请求页面并解析数据

  

  import requests<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />import time<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />for page in range(1, 6):  # 获取5页数据<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />    # 利用format构造URL<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />    url = 'https://www.xfz.cn/api/website/articles/?p={}&n=20&type='.format(page)<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />    # 发送请求获取响应<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />    res = requests.get(url=url)<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />    # 将响应的json格式字符串,解析成为Python字典格式<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />    info_dic = res.json()<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />    # 提取我们想要的数据,并格式化输出<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />    for info in info_dic['data']:<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />        result = {<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />            'title': info['title'],<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />            'date': info['time'],<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />            'keywords': '-'.join(info['keywords']),<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />            'href': 'https://www.xfz.cn/post/' + str(info['uid']) + '.html'<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />        }<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />        print(result)<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />    time.sleep(1)  # 控制访问频率<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />

  执行结果(部分):

  {'title': '「分贝通」完成C+轮1.4亿美元融资', 'date': '2022-02-17 10:17:13', 'keywords': '分贝通-DST Global', 'href': 'https://www.xfz.cn/post/10415.html'}<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />{'title': '「塬数科技」完成近亿元A轮融资,凡卓资本担任独家财务顾问', 'date': '2022-02-15 10:17:42', 'keywords': '塬数科技-凡卓资本-晨山资本-博将资本', 'href': 'https://www.xfz.cn/post/10412.html'}<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />{'title': '「BUD」获1500万美元A+轮融资', 'date': '2022-02-14 10:15:35', 'keywords': '启明创投-源码资本-GGV纪源资本-云九资本', 'href': 'https://www.xfz.cn/post/10411.html'}<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />{'title': '以图计算引擎切入千亿级数据分析市场,它要让人人成为分析师,能否造就国内百亿级黑马', 'date': '2022-02-10 11:04:52', 'keywords': '欧拉认知智能-新一代BI', 'href': 'https://www.xfz.cn/post/10410.html'}<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />{'title': '前有Rivian市值千亿,后有经纬、博原频频押注,滑板底盘赛道将诞生新巨头?丨什么值得投', 'date': '2022-02-09 11:51:36', 'keywords': '什么值得投', 'href': 'https://www.xfz.cn/post/10409.html'}<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />

  2.保存到本地csv

  在原代码的基础上,我们添加了一点内容,并将我们爬取的内容保存到一个CSV文件中。有很多方法可以将其保存到 CSV 文件。这里我们使用pandas的第三方模块来实现,需要pip install pandas。安装。

  import requests<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />import time<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />import pandas as pd  # 导入模块<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /># 创建一个数据集,用来保存数据<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />data_set = [<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />    ('标题', '日期', '关键词', '详情链接'),  # 这边先定义头部内容<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />]<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />for page in range(1, 6):  # 获取5页数据<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />    # 利用format构造URL<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />    url = 'https://www.xfz.cn/api/website/articles/?p={}&n=20&type='.format(page)<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />    # 发送请求获取响应<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />    res = requests.get(url=url)<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />    # 将响应的json格式字符串,解析成为Python字典格式<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />    info_dic = res.json()<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />    # 提取我们想要的数据,并格式化输出<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />    for info in info_dic['data']:<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />        result = {<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />            'title': info['title'],<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />            'date': info['time'],<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />            'keywords': '/'.join(info['keywords']),  # 关键词会含有多个,每个关键词用斜杠隔开<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />            'href': 'https://www.xfz.cn/post/' + str(info['uid']) + '.html'  # 构造详情页url<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />        }<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />        # 获取字典里面的值,并转换成列表<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />        info_list = list(result.values())<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />        # 添加到数据集<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />        data_set.append(info_list)<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />    time.sleep(1)  # 控制访问频率<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /># 保存成为csv文件<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />df = pd.DataFrame(data_set)<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />df.to_csv('xfz.csv', mode='a', encoding='utf-8-sig', header=False, index=False)<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />

  执行结果(部分):

  Part5总结

  文中介绍了动态网站data采集的基本流程和方法,结合上期我们讲的静态网页数据采集实战,相信大家已经掌握了数据采集基本功。那么返回的数据采集 呢?请继续关注下一条推文:Python 数据处理的基础知识。

  附件:get_web_data.py

  import requests<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />import time<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />import pandas as pd  # 导入模块<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /># 创建一个数据集,用来保存数据<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />data_set = [<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />    ('标题', '日期', '关键词', '详情链接'),  # 这边先定义头部内容<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />]<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />for page in range(1, 6):  # 获取5页数据<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />    # 利用format构造URL<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />    url = 'https://www.xfz.cn/api/website/articles/?p={}&n=20&type='.format(page)<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />    # 发送请求获取响应<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />    res = requests.get(url=url)<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />    # 将响应的json格式字符串,解析成为Python字典格式<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />    info_dic = res.json()<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />    # 提取我们想要的数据,并格式化输出<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />    for info in info_dic['data']:<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />        result = {<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />            'title': info['title'],<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />            'date': info['time'],<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />            'keywords': '/'.join(info['keywords']),  # 关键词会含有多个,每个关键词用斜杠隔开<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />            'href': 'https://www.xfz.cn/post/' + str(info['uid']) + '.html'  # 构造详情页url<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />        }<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />        # 获取字典里面的值,并转换成列表<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />        info_list = list(result.values())<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />        # 添加到数据集<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />        data_set.append(info_list)<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />    time.sleep(1)  # 控制访问频率<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /># 保存成为csv文件<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />df = pd.DataFrame(data_set)<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />df.to_csv('xfz.csv', mode='a', encoding='utf-8-sig', header=False, index=False)<br style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />

  直观:源码角度了解Skywalking之Trace信息的生成

  从源代码的角度,了解人行者踪迹信息的生成

  跟踪 Id 是有关分布式链接的信息,通过该链接可以找到链接

  生成跟踪标识

  Skwalking 的跟踪 Id 是使用全局Id*敏*感*词*的生成() 方法生成的

  第 I 部分:具体而言,应用程序实例 ID

  第 2 部分:线程 ID

  第 III 部分:当前线程中的时间戳 *10000+ seq,seq 的值介于 0(包括)和 9999(包括 0)和 9999(包括 10000+秒)之间

  )。

  三部分通过。

  我们知道,SKywalking在涉及研究所方法时启动了拦截实例方法,当它涉及到研究所的关联阿格斯兰杰和研究所内部蓝洁时,兰杰的拦截方法调用了兰杰周围的之前方法()方法,并且此方法调用了上下文管理器的 createSpan() 方法。在方法之后调用上下文管理器的停止Span()方法,让我们看一下上下文管理器类中涉及的方法

  上下文管理器

  虽然上下文管理器也实现了引导服务接口,但引导服务的相关方法是空实现的,上下文管理器创建跨度有三种方法,即创建入口潘()方法、创建本地调度()方法和创建ExitSpan()方法。入口跨度是进入服务时创建的 Span,例如消息队列的使用者入口,LocalSpan 是调用本地方法时创建的 Span,“退出跨度”是离开服务时(例如在发起远程调用时或消息队列生成消息时)时创建的 Span

  上下文管理器的 createEntrySpan() 方法

  上下文管理器的 createEntrySpan() 方法

  公共静态抽象跨度创建入口跨度(字符串操作名称,上下文载体载波) {

  摘要跨度;

  摘要跟踪上下文;

  操作名称 = 字符串工具剪切(操作名称, OPERATION_NAME_THRESHOLD);

  如果 (承运人 != 空 && 国际标准书号.是有效的()) {

  SamplingService samplingService = ServiceManager.INSTANCE.findService(SamplingService.class);

  采样服务强制采样();

  上下文 = 获取或创建(操作名称,真);

  span = 上下文.createEntrySpan(操作名称);

  上下文提取(载体);

  } 否则 {

  上下文 = 获取或创建(操作名称,假);

  span = 上下文.createEntrySpan(操作名称);

  }

  返回跨度;

  }

  如果存在上游服务,请找到采样服务实例并调用强制采样() 方法强制采样

  获取当前线程的跟踪上下文

  

  调用跟踪上下文的创建入口跨度() 方法来创建入口跨度,其中活动跨度堆栈存储活动跨度,如果此堆栈中存在跨度,则将其放置在堆栈中

  提取上游跟踪信息

  如果没有上游服务,请创建入口跨度。

  上下文管理器的停止Span()方法关闭跨度并调用线程本地的 remove() 方法来清除线程本地以防止内存泄漏

  跟踪上下文

  跟踪上下文的创建入口潘() 方法

  公共摘要跨度创建入口跨度(最终字符串操作名称) {

  如果 (是极限机制工作()) {

  顶点跨度 = 新单点跨度();

  回推(跨度);

  }

  摘要跨度条目跨度;

  最终摘要跨度父跨度 = peek();

  最终整数父面板 Id = 父跨度 == 空 ? -1 : 父跨度.getSpanId();

  如果 (父跨度 != 空 && 父跨度是进入 ()) {

  条目跨度 = (抽象跟踪跨度)字典管理器.find端点部分()

  .findonly(segment.getServiceId(), operationName)

  .doInCondition(new PossibleFound.FoundandAndAntain() {

  @Override公共对象 doProcess(int operationId) {

  返回父级跨度.set操作 Id(操作 Id);

  }

  }, 新可能发现.not发现和获取() {

  @Override 公共对象 doProcess() {

  返回父级操作名(操作名);

  }

  });

  返回条目跨度启动();

  } 否则 {

  条目跨度 = (抽象跟踪跨度)字典管理器.find端点部分()

  .findonly(segment.getServiceId(), operationName)

  .doInCondition(new PossibleFound.FoundandAndAntain() {

  @Override公共对象 doProcess(int operationId) {

  返回新的条目跨度(spanId*敏*感*词*++,父跨度 Id,操作 Id);

  

  }

  }, 新可能发现.not发现和获取() {

  @Override 公共对象 doProcess() {

  返回新的条目跨度(spanId*敏*感*词*++,父跨度 Id,操作名称);

  }

  });

  条目跨度启动();

  返回推送(条目跨度);

  }

  }

  让我们来分析一下这篇文章的逻辑

  调用 isLimit 机制工作() 方法确定是否超过了最大跨度数,默认值为 300,如果超过最大数量,则创建一个 NoopSpan 对象,将其放在堆栈上并返回

  如果未超过最大数量,则堆栈顶部的跨度为当前跨度

  如果父跨度不存在,请创建一个入口跨度对象,调用“进入跨度”() 以打开该跨度,start() 方法实际上是将“开始时间”属性值设置为当前时间

  如果父跨度存在,请创建入口跨度对象,打开该跨度,然后将其按入堆栈。

  让我们举个例子来看看如何使用这三种类型的跨度

  服务 A

  公共无效 a() {

  b();

  c();

  d.d();

  }

  服务 A 具有 a() 方法,而 a() 方法

  调用服务自己的 b() 方法、c() 方法和服务 d 的 d() 方法,具体如下:

  请求是通过雄猫插件进入堆栈创建的,并调用 start() 以打开跨度

  调用 b() 方法时

  ,则将本地跨度对象创建到堆栈中,调用 start() 以打开跨度,并且 a() 方法在结束时退出堆栈

  调用 c() 方法时

  ,则在堆栈中创建相同的本地跨度对象,调用 start() 以打开跨度,并且 b() 方法在结束时退出堆栈

  调用 d() 方法时,将在堆栈中创建退出跨度对象,并在服务 b 调用结束时将退出跨度添加到堆栈中

  然后,当条目跨度从堆栈中出来时,a() 结束。

  总结

  在本文章我们主要讨论了当请求通过时如何创建跟踪,有三种类型的跨度,入口跨度是进入此服务时创建的跨度,LocalSpan是调用本地方法时创建的跨度,ExitSpan是通过离开此服务创建的跨度,并且对上下文管理器类进行了深入分析。此类主要完成当前线程和跟踪上下文的绑定,后者是创建 span 的类。

0 个评论

要回复文章请先登录注册


官方客服QQ群

微信人工客服

QQ人工客服


线