技术分享:爬虫 | 如何构建技术文章聚合平台(二)

优采云 发布时间: 2022-11-02 17:22

  技术分享:爬虫 | 如何构建技术文章聚合平台(二)

  作者 | 张马文

  来源 | 掘金

  上一篇文章《教你如何用Crawlab搭建技术文章聚合平台(一)》介绍了如何使用和搭建Crawlab运行环境,并将Puppeteer与Crawlab集成,对掘金很有帮助, SegmentFault , CSDN 爬取技术文章,最后可以查看爬取结果。本文文章将继续讲解如何使用Flask+Vue编写一个精简的聚合平台来展示捕获到的文章内容。

  文章内容爬虫

  首先,我们需要对爬虫部分做一个小的补充。在上一篇文章中,我们只写了一个爬取文章网址的爬虫。我们还需要爬取文章内容,所以也需要编写爬虫这部分。上次爬虫的结果集合全部改成results,文章的内容会保存在数据库的content字段中。

  经过分析,我们知道每个技术网站的文章页面都有一个固定的标签,抓取这个标签下的所有HTML就可以了。具体代码分析就不展开了,这里贴出具体代码。

  <p>const puppeteer = require('puppeteer');

  const MongoClient = require('mongodb').MongoClient;

  (async => {

  // browser

  const browser = await (puppeteer.launch({

  headless: true

  }));

  // page

  const page = await browser.newPage;

  // open database connection

  const client = await MongoClient.connect('mongodb://192.168.99.100:27017');

  let db = await client.db('crawlab_test');

  const colName = process.env.CRAWLAB_COLLECTION || 'results';

  const col = db.collection(colName);

  const col_src = db.collection('results');

  const results = await col_src.find({content: {$exists: false}}).toArray;

  for (let i = 0; i < results.length; i++) {

  let item = results[i];

  // define article anchor

  let anchor;

  if (item.source === 'juejin') {

  anchor = '.article-content';

  } else if (item.source === 'segmentfault') {

  anchor = '.article';

  } else if (item.source === 'csdn') {

  anchor = '#content_views';

  } else {

  continue;

  }

  console.log(`anchor: ${anchor}`);

  // navigate to the article

  try {

  await page.goto(item.url, {waitUntil: 'domcontentloaded'});

  await page.waitFor(2000);

  } catch (e) {

  console.error(e);

  continue;

  }

  // scrape article content

  item.content = await page.$eval(anchor, el => el.innerHTML);

  // save to database

  await col.save(item);

  console.log(`saved item: ${JSON.stringify(item)}`)

  }

  // close mongodb

  client.close;

  // close browser

  browser.close;

  });</p>

  然后按照上一篇文章的步骤部署运行爬虫,就可以采集到详细的文章内容了。

  文章内容爬虫的代码已经更新到Github了。

  接下来,我们可以开始对这些 文章 做 文章。

  前后分离

  从目前的技术发展来看,前后端分离已经成为主流:一是前端技术越来越复杂,需要模块化和工程化;第二,前后端分离,让前后端团队分工协作,更高效地开发应用。由于本文的聚合平台是一个轻量级的应用,所以我们使用Python的轻量级Web应用框架Flask来编写后端界面,而我们使用的是近年来非常流行的Vue,前端也很容易上手-结尾。

  烧瓶

  Flask全称为Micro Framework,可见其轻量级,几行代码就能写出一个Web应用。它依赖扩展插件来扩展其特定功能,例如登录身份验证、RESTful、数据模型等。在本节中,我们将构建一个 RESTful 后端 API 应用程序。

  安装

  首先安装相关的依赖。

  <p>pip install flask flask_restful flask_cors pymongo</p>

  基本应用

  安装完成后,我们可以新建一个app.py文件,输入如下代码

  <p>from flask import Flask

  from flask_cors import CORS

  from flask_restful import Api

  # 生成Flask App实例

  app = Flask(__name__)

  # 生成API实例

  api = Api(app)

  # 支持CORS跨域

  CORS(app, supports_credentials=True)

  if __name__ == '__main__':

  app.run</p>

  这个基本的 Flask 应用程序可以通过在命令行输入 python app.py 来运行。

  编写 API

  接下来,我们需要编写获取文章的接口。首先,让我们简要分析一下需求。

  这个 Flask 应用程序要实现的功能是:

  从数据库中获取捕获的文章,并将文章ID、标题、摘要、捕获时间返回给前端,作为文章列表使用;

  

  对于给定的 文章ID,从数据库中返回相应的 文章 内容,以供前端用作详细信息页面。

  因此,我们需要实现上述两个API。让我们开始编写界面。

  列表界面

  在 app.py 中添加如下代码作为列表界面。

  <p>class ListApi(Resource):

  def get(self):

  # 查询

  items = col.find({'content': {'$exists': True}}).sort('_id', DESCENDING).limit(40)

  data =

  for item in items:

  # 将pymongo object转化为python object

  _item = json.loads(json_util.dumps(item))

  data.append({

  '_id': _item['_id']['$oid'],

  'title': _item['title'],

  'source': _item['source'],

  'ts': item['_id'].generation_time.strftime('%Y-%m-%d %H:%M:%S')

  })

  return data</p>

  详情界面

  同样,在 app.py 中输入以下代码。

  <p>class DetailApi(Resource):

  def get(self, id):

  item = col.find_one({'_id': ObjectId(id)})

  # 将pymongo object转化为python object

  _item = json.loads(json_util.dumps(item))

  return {

  '_id': _item['_id']['$oid'],

  'title': _item['title'],

  'source': _item['source'],

  'ts': item['_id'].generation_time.strftime('%Y-%m-%d %H:%M:%S'),

  'content': _item['content']

  }</p>

  映射接口

  编写好接口后,我们需要将它们映射到相应的 URL。

  <p>api.add_resource(ListApi, '/results')

  api.add_resource(DetailApi, '/results/')</p>

  完整代码

  下面是完整的Flask应用代码,非常简单,实现了文章list和文章details两个功能。接下来,我们将开始开发前端部分。

  <p>import json

  from bson import json_util, ObjectId

  from flask import Flask, jsonify

  from flask_cors import CORS

  from flask_restful import Api, Resource

  from pymongo import MongoClient, DESCENDING

  # 生成Flask App实例

  app = Flask(__name__)

  # 生成MongoDB实例

  mongo = MongoClient(host='192.168.99.100')

  db = mongo['crawlab_test']

  col = db['results']

  # 生成API实例

  api = Api(app)

  # 支持CORS跨域

  CORS(app, supports_credentials=True)

  class ListApi(Resource):

  def get(self):

  # 查询

  items = col.find({}).sort('_id', DESCENDING).limit(20)

  data =

  for item in items:

  # 将pymongo object转化为python object

  _item = json.loads(json_util.dumps(item))

  data.append({

  '_id': _item['_id']['$oid'],

  'title': _item['title'],

  'source': _item['source'],

  'ts': item['_id'].generation_time.strftime('%Y-%m-%d %H:%M:%S')

  })

  return data

  class DetailApi(Resource):

  def get(self, id):

  item = col.find_one({'_id': ObjectId(id)})

  # 将pymongo object转化为python object

  _item = json.loads(json_util.dumps(item))

  return {

  '_id': _item['_id']['$oid'],

  'title': _item['title'],

  'source': _item['source'],

  'ts': item['_id'].generation_time.strftime('%Y-%m-%d %H:%M:%S'),

  'content': _item['content']

  }

  api.add_resource(ListApi, '/results')

  api.add_resource(DetailApi, '/results/')

  if __name__ == '__main__':

  app.run</p>

  运行 python app.py 运行后台接口服务器。

  Vue

  Vue这几年非常火。它已经在 Github 上超越 React,成为三大开源框架(React、Vue 和 Angular)中 Star 最多的项目。与 React 和 Angular 相比,Vue 非常容易上手。您可以使用双向绑定数据快速开始构建简单的应用程序,也可以使用 Vuex 单向数据传输构建大型应用程序。这种灵活性是它受到大多数开发人员欢迎的原因之一。

  为了构建一个简单的 Vue 应用程序,我们将使用 vue-cli3,一个 Vue 项目的脚手架。首先,我们从 npm 安装脚手架。

  安装 vue-cli3

  <p>yarn add @vue/cli</p>

  如果你还没有安装yarn,执行下面的命令安装。

  <p>npm i -g yarn</p>

  创建项目

  接下来,我们需要使用 vue-cli3 构建一个项目。执行以下命令。

  <p>vue create frontend</p>

  命令行会弹出以下选项,选择默认即可。

  

  <p>? Please pick a preset: (Use arrow keys)

  ❯ default (babel, eslint)

  preset (vue-router, vuex, node-sass, babel, eslint, unit-jest)

  Manually select features </p>

  然后 vue-cli3 将开始准备必要的依赖项来构建项目并生成项目结构。

  此外,我们还需要安装完成其他功能所需的包。

  <p>yarn add axios</p>

  文章列表页面

  在 views 目录中创建一个 List.vue 文件,内容如下。

  <p>

  {{article.title}}

  

  {{article.ts}}

  import axios from 'axios'

  export default {

  name: 'List',

  data {

  return {

  list:

  }

  },

  methods: {

  showArticle (id) {

  this.$router.push(`/${id}`)

  }

  },

  created {

  axios.get('http://localhost:5000/results')

  .then(response => {

  this.list = response.data

  })

  }

  }

  .list {

  display: flex;

  }

  .left {

  flex-basis: 20%;

  }

  .right {

  flex-basis: 20%;

  }

  .article-list {

  text-align: left;

  list-style: none;

  }

  .article-item {

  background: #c3edfb;

  border-radius: 5px;

  padding: 5px;

  height: 32px;

  display: flex;

  align-items: center;

  justify-content: space-between;

  margin-bottom: 10px;

  }

  .title {

  flex-basis: auto;

  color: #58769d;

  }

  .time {

  font-size: 10px;

  text-align: right;

  flex-basis: 180px;

  }</p>

  其中,ajax与API交互引用axios,这里获取list接口。该布局用于经典的双圣杯布局。methods中的showArticle方法接收到id参数,跳转到详情页。

  文章详情页面

  在views目录下,创建一个Detail.vue文件,输入如下内容。

  <p>{{article.title}}

  import axios from 'axios'

  export default {

  name: 'Detail',

  data {

  return {

  article: {}

  }

  },

  computed: {

  id {

  return this.$route.params.id

  }

  },

  created {

  axios.get(`http://localhost:5000/results/${this.id}`)

  .then(response => {

  this.article = response.data

  })

  }

  }

  .detail {

  display: flex;

  }

  .left {

  flex-basis: 20%;

  }

  .right {

  flex-basis: 20%;

  }

  .center {

  flex-basis: 60%;

  text-align: left;

  }

  .title {

  }</p>

  这个页面也是经典的双圣杯布局,中间占40%。API获取的文章内容输出到content,并通过v-html绑定。其实这里可以做进一步的CSS优化,只是作者太懒了,这个任务留给读者自己去完成。

  添加路线

  编辑 router.js 文件并将其修改为以下内容。

  <p>import Vue from 'vue'

  import Router from 'vue-router'

  import List from './views/List'

  import Detail from './views/Detail'

  Vue.use(Router)

  export default new Router({

  mode: 'hash',

  base: process.env.BASE_URL,

  routes: [

  {

  path: '/',

  name: 'List',

  component: List

  },

  {

  path: '/:id',

  name: 'Detail',

  component: Detail

  }

  ]

  })</p>

  运行前端

  在命令行中输入以下命令,打开:8080可以看到文章的列表。

  <p>npm run serve</p>

  最终效果

  最终聚合平台效果截图如下,可以看到基本样式已经出来了。

  总结

  本文在上一篇文章《教你如何用Crawlab搭建技术文章聚合平台(一)》的基础上,介绍了Flask+Vue的使用方法以及之前抓取的文章数据,搭建一个简单的技术文章聚合平台。使用的技术非常基础。当然,肯定还有很大的优化和改进空间。这是留给读者和大人物的。

  直观:通过网络爬虫采集大数据

  2021-07-02

  网络数据采集是指通过网络爬虫或网站公共API从网站获取数据信息。该方法可以从网页中提取非结构化数据,存储为统一的本地数据文件,并以结构化的方式存储。支持图片、音频、视频等文件或附件的采集,附件可以自动与文本关联。

  在互联网时代,网络爬虫主要为搜索引擎提供最全面、最新的数据。

  本节首先简要介绍网络爬虫的原理和工作流程,然后讨论网络爬虫的爬取策略,最后介绍典型的网络工具。

  网络爬虫的原理

  网络爬虫是根据一定的规则自动爬取网络信息的程序或脚本。

  网络爬虫可以自动采集所有可以访问的页面内容,为搜索引擎和大数据分析提供数据源。从功能上来说,爬虫一般具有数据采集、处理和存储三个功能,如图1所示。

  图1 网络爬虫*敏*感*词*

  除了供用户阅读的文字信息外,网页还收录一些超链接信息。

  网络爬虫系统正是通过网页中的超链接信息不断获取网络上的其他网页。网络爬虫从一个或多个初始网页的URL开始,获取初始网页上的URL。在爬取网页的过程中,不断地从当前页面中提取新的 URL 并放入队列中,直到满足系统的某些停止条件。

  网络爬虫系统一般会选择一些比较重要的、出度(网页链接出的超链接数)网站较大的URL作为*敏*感*词*URL集。

  网络爬虫系统使用这些*敏*感*词*集作为初始 URL 来开始数据爬取。因为网页中收录链接信息,所以会通过已有网页的URL获取一些新的URL。

  网页之间的指向结构可以看成一片森林,每个*敏*感*词*URL对应的网页就是森林中一棵树的根节点,这样网络爬虫系统就可以按照广度优先搜索算法遍历所有信息或深度优先搜索算法。网页。

  由于深度优先搜索算法可能导致爬虫系统陷入网站内部,不利于搜索距离网站首页比较近的网页信息,因此广度优先搜索算法一般使用采集网页。

  网络爬虫系统首先将*敏*感*词* URL 放入下载队列,简单地从队列头部取一个 URL 下载其对应的网页,获取网页内容并存储,然后解析链接信息网页以获取一些新的 URL。

  其次,根据一定的网页分析算法,过滤掉与主题无关的链接,保留有用的链接,放入待抓取的URL队列中。

  最后取出一个URL,下载其对应的网页,然后解析,以此类推,直到遍历全网或者满足某个条件。

  网络爬虫工作流程

  如图 2 所示,网络爬虫的基本工作流程如下。

  1) 首先选择*敏*感*词* URL 的一部分。

  2)将这些网址放入待抓取的网址队列中。

  3)从待爬取URL队列中取出待爬取URL,解析DNS,获取主机IP地址,下载该URL对应的网页,存入下载的网页库中。此外,将这些 URL 放入 Crawl URLs 队列。

  4)分析URL队列中已经爬取的URL,分析其中的其他URL,将这些URL放入待爬取的URL队列,从而进入下一个循环。

  图2 网络爬虫基本工作流程

  网络爬虫抓取策略

  谷歌、百度等常见搜索引擎抓取的网页数量通常以数十亿计。那么,面对如此多的网页,网络爬虫如何才能尽可能的遍历所有网页呢?如果您对大数据开发感兴趣,想系统地学习大数据,可以加入大数据技术学习交流群:458号345号782获取学习资源,从而扩大网页信息的覆盖范围可能,这是网络爬虫系统面临的一个非常关键的问题。在网络爬虫系统中,爬取策略决定了网页被爬取的顺序。

  本节首先简要介绍网络爬取策略中使用的基本概念。

  1)网页之间的关系模型

  从互联网的结构来看,网页通过各种超链接相互连接,形成一个巨大而复杂的相互关联的有向图。

  

  如图3所示,如果把网页看成图中的一个节点,把网页中其他网页的链接看成这个节点到其他节点的边,那么我们就可以轻松查看整个互联网网页被建模为有向图。

  理论上,通过遍历算法对图进行遍历,几乎可以访问互联网上的任何网页。

  图3 网页关系模型图

  2)网页分类

  从爬虫的角度来划分互联网,可以将互联网的所有页面分为5个部分:已下载未过期网页、已下载已过期网页、待下载网页、已知网页和未知网页,如图4.

  本地爬取的网页实际上是互联网内容的镜像和备份。互联网正在动态变化。当互联网的一部分内容发生变化时,本地抓取的网页就会失效。因此,下载的网页分为两类:下载的未过期网页和下载的过期网页。

  图4 网页分类

  要下载的页面是 URL 队列中要抓取的页面。

  可以看出,网页是指尚未被爬取且不在待爬取URL队列中的网页,但可以通过分析爬取的页面或待爬取URL对应的页面得到。

  还有一些网页是网络爬虫无法直接爬取下载的,称为不可知网页。

  下面重点介绍几种常见的爬取策略。

  1.万能网络爬虫

  通用网络爬虫也称为全网爬虫。爬取对象从一些*敏*感*词*URL延伸到整个网络,主要针对门户网站搜索引擎和大型网络服务商采集数据。

  为了提高工作效率,一般的网络爬虫都会采用一定的爬取策略。常用的爬取策略有深度优先策略和广度优先策略。

  1) 深度优先策略

  深度优先策略意味着网络爬虫将从起始页面开始,并逐个链接地跟踪它,直到无法再深入为止。

  完成一个爬取分支后,网络爬虫返回上一个链接节点,进一步搜索其他链接。当所有的链接都遍历完后,爬取任务结束。

  这种策略比较适合垂直搜索或者站内搜索,但是在抓取页面内容比较深的网站时会造成巨大的资源浪费。

  以图3为例,遍历的路径为1→2→5→6→3→7→4→8。

  在深度优先策略中,当搜索一个节点时,该节点的子节点和子节点的后继节点都在该节点的兄弟节点之前,深度优先策略在搜索空间中。有时,它会尝试尽可能深入,并且仅在找不到节点的后继节点时才考虑其兄弟节点。

  这样的策略决定了深度优先策略不一定能找到最优解,甚至由于深度的限制而无法找到解。

  如果不加以限制,它将沿着一条路径无限扩展,这将“捕获”成大量数据。一般来说,使用深度优先策略会选择一个合适的深度,然后反复搜索直到找到一个解,这样会降低搜索的效率。因此,当搜索数据量较小时,一般采用深度优先策略。

  2) 广度优先策略

  广度优先策略根据网页内容目录层次的深度对页面进行爬取,较浅的目录层次的页面先爬取。当同一级别的页面被爬取时,爬虫进入下一级继续爬取。

  还是以图3为例,遍历的路径是1→2→3→4→5→6→7→8

  由于广度优先策略是在第 N 层的节点扩展完成后进入第 N+1 层,保证了通过最短路径找到解。

  该策略可以有效控制页面的爬取深度,避免遇到无限深分支时无法结束爬取的问题。易于实现,不需要存储大量的中间节点。缺点是爬到更深的目录级别需要很长时间。页。

  如果搜索的分支太多,即节点的后继节点太多,算法就会耗尽资源,在可用空间中找不到解。

  2.专注于网络爬虫

  聚焦网络爬虫,也称为主题网络爬虫,是选择性地爬取与预定义主题相关的页面的网络爬虫。

  

  1)基于内容评价的爬取策略

  DeBra 将文本相似度的计算方法引入网络爬虫,提出了 Fish Search 算法。

  该算法以用户输入的查询词为主题,将收录查询词的页面视为与该主题相关的页面,其局限性在于无法评估该页面与该主题的相关性。

  Herseovic 对 Fish Search 算法进行了改进,提出了 Shark Search 算法,该算法使用空间向量模型来计算页面和主题之间的相关度。

  通过采用基于连续值计算链接值的方法,我们不仅可以计算出哪些捕获的链接与主题相关,而且可以得到相关性的量化大小。

  2)基于链接结构评估的爬取策略

  与普通文本不同,网页是收录大量结构化信息的半结构化文档。

  网页不是单独存在的。页面中的链接表示页面之间的关系。基于链接结构的搜索策略模式利用这些结构特征来评估页面和链接的重要性,从而确定搜索顺序。其中,PageRank算法就是这种搜索策略模式的代表。

  PageRank算法的基本原理是,如果一个网页被多次引用,它可能是一个重要的网页;如果一个网页没有被多次引用,而是被一个重要网页引用,那么它也可能是一个重要网页。一个网页的重要性同样传递给它所指的网页。

  链接页面的PageRank是通过将某个页面的PageRank除以该页面上存在的前向链接,并将得到的值分别与前向链接所指向的页面的PageRank相加得到。

  如图 5 所示,PageRank 为 100 的页面将其重要性平等地传递给它所引用的两个页面,每个页面获得 50,而 PageRank 为 9 的同一页面将其重要性传递给它所引用的三个页面。页面的每一页都传递一个值 3。

  PageRank 为 53 的页面的值源自引用它的两个页面传递的值。

  ,

  图5 PageRank算法示例

  3)基于强化学习的爬取策略

  Rennie 和 McCallum 将强化学习引入聚焦爬虫,使用贝叶斯分类器根据整个网页文本和链接文本对超链接进行分类,并计算每个链接的重要性以确定链接被访问的顺序。

  4)基于上下文图的爬取策略

  勤勉等人。提出了一种爬取策略,通过构建上下文图来学习网页之间的相关性。该策略可以训练一个机器学习系统,通过该系统可以计算当前页面到相关网页的距离。中的链接具有优先访问权。

  3.增量网络爬虫

  增量网络爬虫是指对下载的网页进行增量更新,只爬取新生成或更改的网页的爬虫。可以在一定程度上保证爬取的页面尽可能的新。

  增量网络爬虫有两个目标:

  为了实现第一个目标,增量网络爬虫需要通过重访网页来更新本地页面集中的页面内容。常用的方法有统一更新法、个体更新法和分类更新法。

  为了实现第二个目标,增量网络爬虫需要对网页的重要性进行排名。常见的策略包括广度优先策略和PageRank优先策略。

  4. 深网爬虫

  网页按存在方式可分为表层网页和深层网页。

  深网爬虫架构由六个基本功能模块(爬取控制器、解析器、表单分析器、表单处理器、响应分析器、LVS控制器)和两个爬虫内部数据结构(URL列表和LVS表)组成。

  其中,LVS(LabelValueSet)表示标签和值的集合,用来表示填写表格的数据源。在爬取过程中,最重要的部分是表单填写,包括基于领域知识的表单填写和基于网页结构分析的表单填写。

  分类:

  技术要点:

  相关文章:

0 个评论

要回复文章请先登录注册


官方客服QQ群

微信人工客服

QQ人工客服


线