发布于 2026-01-06 3 阅读
0

使用 React-Native 抓取网站

使用 React-Native 抓取网站

多年 Web 开发经验让我对 React Native 有了全新的认识,感觉就像开启了新篇章。它不仅能让你更好地使用原生功能,还能减少应用的限制。例如,你可以使用fetch()toy 来获取任何你想要的网站。这使得客户端网站爬虫成为可能。

为什么

也许你需要从某个服务获取数据,但他们没有开放 API,或者 API 无法提供你所需的所有数据,又或者 API 本身就不好用。通常情况下,你需要搭建一个服务器来抓取目标网站并将其转换为可用的 API,但如果你能直接在客户端访问所有网站的全部数据,就能节省大量时间。

以亚马逊网站为例。你想显示页面上的所有产品,并提供一种加载下一页的方法,但你想把它放在我们自己的数据结构中,这样你就可以围绕它构建自己的用户界面。

如何

  1. 从服务器获取 HTML
  2. 从HTML中提取所需数据
  3. 重新整理数据以供我们使用

1. 从服务器获取 HTML

那只是最简单的部分。

async function loadGraphicCards(page = 1) {
  const searchUrl = `https://www.amazon.de/s/?page=${page}&keywords=graphic+card`;
  const response = await fetch(searchUrl);   // fetch page

  const htmlString = await response.text();  // get response text
  ...
}
Enter fullscreen mode Exit fullscreen mode

使用搜索模式获取 URL 会返回一个包含一些项目的 HTML 页面。

2. 从HTML中提取所需数据

这有点棘手。数据在HTML代码里,但它是一个字符串。

最简单的办法是使用正则表达式来解析字符串并获取数据,但 HTML 没有正则语法,所以这种方法行不通。

更好的方法是使用 HTML 解析器和 CSS 选择器。

Cheerio就是这个解决方案。它自带 HTML 解析器,并重新实现了 jQuery 的核心功能,因此你可以在 Node.js 上使用它。

问题是,React-Native 缺少大部分 Node.js 包,所以无法正常工作。

我花了很长时间才找到一个可以在 React-Native 上运行的 Cheerio 的重新实现版本,这个包的命名有点奇怪,哈哈。

但有了这项技术,数据提取也变得轻而易举了。

async function loadGraphicCards(page = 1) {
  const searchUrl = `https://www.amazon.de/s/?page=${page}&keywords=graphic+card`;
  const response = await fetch(searchUrl);      // fetch page 

  const htmlString = await response.text();     // get response text
  const $ = cheerio.load(htmlString);           // parse HTML string

  const liList = $("#s-results-list-atf > li"); // select result <li>s
  ...
}
Enter fullscreen mode Exit fullscreen mode

3. 重塑数据以供后续使用

从 HTML 中提取数据后,我们就可以开始根据我们的使用场景对其进行重塑。提取和重塑之间的界限在这里有些模糊,<li>我们选择的元素包含大量的标记,从中提取正确的数据也属于提取过程,但通常这两个步骤是相辅相成的。

async function loadGraphicCards(page = 1) {
  const searchUrl = `https://www.amazon.de/s/?page=${page}&keywords=graphic+card`;
  const response = await fetch(searchUrl);  // fetch page 

  const htmlString = await response.text(); // get response text
  const $ = cheerio.load(htmlString);       // parse HTML string

  return $("#s-results-list-atf > li")             // select result <li>s
    .map((_, li) => ({                      // map to an list of objects
      asin: $(li).data("asin"),                   
      title: $("h2", li).text(),                
      price: $("span.a-color-price", li).text(),
      rating: $("span.a-icon-alt", li).text(),
      imageUrl: $("img.s-access-image").attr("src")
    }));
}
Enter fullscreen mode Exit fullscreen mode

这虽然不是一个全面的例子,但我想你应该明白我的意思了。现在我们可以利用应用程序中新增的对象列表,为亚马逊的搜索结果创建我们自己的用户界面。


class App extends ReactComponent {
  state = {
    page: 0,
    items: [],
  };

  componentDidMount = () => this.loadNextPage();

  loadNextPage = () =>
    this.setState(async state => {
      const page = state.page + 1;
      const items = await loadGraphicCards(page);
      return {items, page};
    });

  render = () => (
    <ScrollView>
      {this.state.items.map(item => <Item {...item} key={item.asin}/>)}
    </ScrollView>
  );
}

const Item = props => (
  <TouchableOpacity onPress={() => alert("ASIN:" + props.asin)}>
    <Text>{props.title}</Text>
    <Image source={{uri: props.imageUrl}}/>
    <Text>{props.price}</Text>
    <Text>{props.rating}</Text>
  </TouchableOpacity>
);
Enter fullscreen mode Exit fullscreen mode

结论

和大多数问题一样,只要拥有合适的工具,解决方案就会变得简单。通常,问题更多在于如何找到这些工具 :D

这种客户端爬虫方法无需 API 即可快速构建原型。亚马逊提供的静态 HTML 还算不错,所以这种方法在他们的网站上效果很好。

文章来源:https://dev.to/kayis/crawling-websites-in-react-native-38b