React Native网页数据抓取与解析实现

优采云 发布时间: 2022-05-11 08:51

  React Native网页数据抓取与解析实现

  RN 提供了Fetch API和 XMLHttpRequest API,基于这两个库的二次封装库也是可以用的,比如 frisbee和axios,所以在 RN 下进行 HTTP 请求不是什么问题。

  HTML 解析

  当前,最好用的 js html parser 应属cheerio,是否可以在 RN 使用呢?让我们试试。

  首先,安装 cherrio(注意,一定是要 v0.22.0,后面解释):

  <p style="margin-left: 8px;margin-right: 8px;letter-spacing: 0.5px;line-height: 1.75em;margin-bottom: 10px;">$ npm i cheerio@0.22.0</p>

  使用:

  <p style="margin-left: 8px;margin-right: 8px;letter-spacing: 0.5px;line-height: 1.75em;margin-bottom: 10px;">import cheerio from 'cheerio'const $ = cheerio.load('Hello world')</p>

  很不幸,出现了错误:

  <p style="margin-left: 8px;margin-right: 8px;letter-spacing: 0.5px;line-height: 1.75em;margin-bottom: 10px;">error: bundling failed: "Unable to resolve module `events`</p>

  这是因为 cheerio 的依赖 htmlparser2 依赖一些 node 内置的库。不过这是可以被解决的,理论上,只要这些依赖库不依赖更底层的接口,那么就可以通过 npm 安装上这些依赖:

  <p style="margin-left: 8px;margin-right: 8px;letter-spacing: 0.5px;line-height: 1.75em;margin-bottom: 10px;">$ npm i events stream buffer</p>

  再次刷新,我们发现 cheerio 已经可以正常使用了!

  其实这个问题有在 cheerio 的 issues 上讨论过:。有人为了解决这个问题弄了另外一个库cheerio-without-node-native,然而这种做法不仅没有必要而且非常糟糕,因为这个分裂出去的版本的质量是难以保证的。作者的观点是:

  You can install the missing packages from npm (events, stream and utils afaict) and they will be automatically picked up.

  I would not recommend the usage of a fork as it will make it difficult to track down issues and will delay, if not prevent, patches for bugs.

  至于为什么只能用 cheerio@0.22.0,是因为之后的版本,cheerio 引入了parse5,而 parse5 依赖 stream.Writable,npm 安装的 stream 并不提供。

  测试

  由于网页随时可能发生变化,测试就显得尤为重要。这里我以一段获取简书用户数据的代码为例,做一个简单的黑箱测试。

  <p style="margin-left: 8px;margin-right: 8px;letter-spacing: 0.5px;line-height: 1.75em;margin-bottom: 10px;">// api.js// 这里,我实现了一个 getUserData 函数,以 UserID 为参数,// 获取个人主页数据,并解析出用户头像链接、用户昵称、发表的文章async function getUserData(user) {  const response = await fetch('http://www.jianshu.com/u/' + user)  const $ = cheerio.load(await response.text())  return {    avatar: 'http:' + $('.avatar img').attr('src'),    name: $('.title .name').text(),    articles: $('.note-list li').map(function () {      return {        title: $('.title', this).text(),

      }

    }).get()

  }

}export {getUserData}</p>

  为了能在 node 环境下使用 fetch,需要安装 node-fetch。RN 已经默认安装了 jest,我们就用它来测试吧:

  <p style="margin-left: 8px;margin-right: 8px;letter-spacing: 0.5px;line-height: 1.75em;margin-bottom: 10px;">// __test__/api.js// 测试 getUserData 是否能正常运行,并返回预期的结果// 这里为了更真实的模拟实际情况,而用 node-fetch 模拟了 RN 里的 fetch// 也可以 mock fetch 然后返回预设的测试数据import {getUserData} from '../api'global.fetch = require('node-fetch')1test('getUserData', async () => {  const data = await getUserData('3747663284a0')<br />2  expect(data.name).toBe('7c00')<br />3  expect(data.avatar).toMatch(/http:\/\/upload\.jianshu\.io\/users\/upload_avatars.*\.jpg/)<br />4  data.articles.forEach(article => expect(article.title).not.toBeNull())  console.log(data)<br />5})<br /></p>

  运行测试:

  <p style="margin-left: 8px;margin-right: 8px;letter-spacing: 0.5px;line-height: 1.75em;margin-bottom: 10px;">$ npm test</p>

  

  npm test

  另一种获取网页数据的黑科技

  除了传统的 HTML 请求解析,在某些情况下我们还可以用类似 PhantomJS 的方案,优点是可以很好地避开一些限制,降低解析难度。RN 里当然用不了 PhantomJS,但我们有 WebView,可以通过 injectedJavaScript 注入 js,用 postMessage 回传数据,比如这段用于获取页面中视频链接的代码:

  1 this._loaded(event.nativeEvent.data)}<br />2  source={{    uri: this.state.webViewUrl,    headers: {      referer: 'https://newplayer.jfrft.com',<br />3    }<br />4  }}<br />5/><br />

  PS. 慎用该方法,首先是 WebView 消耗资源太大,其次是难以测试,缺乏稳定性。

  本文摘自异步社区,作者:xiangzhihong作品:《React Native网页数据抓取与解析实现》,未经授权,禁止转载。

  

0 个评论

要回复文章请先登录注册


官方客服QQ群

微信人工客服

QQ人工客服


线