在本教程中,我们将了解如何使用Java从任何电子商务网站中提取产品数据。 产品数据提取有很多不同的用例,例如:
- 电子商务价格监控
- 价格比较器
- 可用性监控
- 提取评论
- 市场调查
- 违反MAP
我们将从以下产品页面中提取以下不同字段:价格、产品名称、图像URL、SKU和货币:
Https://www.asos.com/the-north-face/the-north-face-vault-backpack-28-litres-in-black/prd/10253008
您需要什么
我们将使用HtmlUnit执行HTTP请求并解析DOM,并将此依赖项添加到pom.xml中。
<dependency>
<groupId>net.sourceforge.htmlunit</groupId>
<artifactId>htmlunit</artifactId>
<version>2.19</version>
</dependency>
我们还将使用Jackson库:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.8</version>
</dependency>
Schema.org
为了提取我们感兴趣的字段,我们将解析Html标记中的https://schema.org元数据。
模式是可以添加到任何网页的语义词汇表。实现模式有很多好处。大多数搜索引擎都使用它来了解页面的含义(产品、文章、评论等等)
根据schema.org的数据,全球大约有1000万个网站在使用它。太好了!
模式有不同的类型,今天我们将研究产品类型
这真的很方便,因为一旦您编写了一个提取特定模式数据的抓取器,它就会在使用相同模式的任何其他网站上运行。 不再需要编写特定的XPath / CSS选择器!
根据我在PricingBot(我以前的公司)中的经验,大约40%的电子商务网站在其DOM中使用schema.org元数据。
有三种主要的架构标记:
JSON-LD
<script type="application/ld+json">
{
"@context": "http://schema.org",
"@type": "ItemList",
"url": "http://multivarki.ru?filters%5Bprice%5D%5BLTE%5D=39600",
"numberOfItems": "315",
"itemListElement": [
{
"@type": "Product",
"image": "http://img01.multivarki.ru.ru/c9/f1/a5fe6642-18d0-47ad-b038-6fca20f1c923.jpeg",
"url": "http://multivarki.ru/brand_502/",
"name": "Brand 502",
"offers": {
"@type": "Offer",
"price": "4399 p."
}
},
{
"@type": "Product",
"name": "..."
}
]
}
</script>
RDF-A
<div vocab="http://schema.org/" typeof="ItemList">
<link property="url" href="http://multivarki.ru?filters%5Bprice%5D%5BLTE%5D=39600"><span property="numberOfItems">315</span>
<div property="itemListElement" typeof="Product">
<img property="image" alt="Photo of product" src="http://img01.multivarki.ru.ru/c9/f1/a5fe6642-18d0-47ad-b038-6fca20f1c923.jpeg"> <a property="url" href="http://multivarki.ru/brand_502/"><span property="name">BRAND 502</span></a>
<div property="offers" typeof="http://schema.org/Offer">
<meta property="schema:priceCurrency" content="RUB">руб
<meta property="schema:price" content="4399.00">4 399,00
<link property="schema:itemCondition" href="http://schema.org/NewCondition">
</div>...
<div property="itemListElement" typeof="Product">
...
</div>
</div>
</div>
在我们的例子中使用的是微数据:
<div class="schema-org">
<div itemscope="" itemtype="https://schema.org/Product">
<img itemprop="image" src="https://images.asos-media.com/products/the-north-face-vault-backpack-28-litres-in-black/10253008-1-black" alt="Image 1 of The North Face Vault Backpack 28 Litres in Black">
<link itemprop="itemCondition" href="https://schema.org/NewCondition">
<span itemprop="productID">10253008</span>
<span itemprop="sku">10253008</span>
<span itemprop="brand" itemscope="" itemtype="https://schema.org/Brand">
<span itemprop="name">The North Face</span>
</span>
<span itemprop="name">The North Face Vault Backpack 28 Litres in Black</span>
<span itemprop="description">Shop The North Face Vault Backpack 28 Litres in Black at ASOS. Discover fashion online.</span>
<span itemprop="offers" itemscope="" itemtype="https://schema.org/Offer">
<link itemprop="availability" href="https://schema.org/InStock">
<meta itemprop="priceCurrency" content="GBP">
<span itemprop="price">60</span>
<span itemprop="eligibleRegion">GB</span>
<span itemprop="seller" itemscope="" itemtype="https://schema.org/Organization">
<span itemprop="name">ASOS</span>
</span>
</span>
</div>
</div>
请注意,您可以在一个页面中包含多个优惠。
提取数据
首先是创建产品的基本POJO:
public class Product {
private BigDecimal price;
private String name;
private String sku;
private URL imageUrl;
private String currency;
// ...getters & setters
然后,我们需要转到目标URL并创建一个基本的微数据解析器来提取我们感兴趣的字段。为此,我正在使用HtmlUnit,这是一个纯Java无头浏览器。 我本可以使用许多不同的库,例如Jsoup或Selenium + Headless Chrome。
但是在大多数情况下,HtmlUnit是一个不错的解决方案,因为它比Selenium + Headless Chrome轻,但比原始HTTP客户端+ JSoup(仅处理HTML解析)提供更多功能。
对于依赖Java的网站,依靠React / Vue.js之类的前端框架,Headless Chrome是必经之路!
WebClient client = new WebClient();
client.getOptions().setCssEnabled(false);
client.getOptions().setJavaScriptEnabled(false);
String productUrl = "https://www.asos.com/the-north-face/the-north-face-vault-backpack-28-litres-in-black/prd/10253008";
HtmlPage page = client.getPage(productUrl);
HtmlElement productNode = ((HtmlElement) page
.getFirstByXPath("//*[@itemtype='https://schema.org/Product']"));
URL imageUrl = new URL((((HtmlElement) productNode.getFirstByXPath("./img")))
.getAttribute("src"));
HtmlElement offers = ((HtmlElement) productNode.getFirstByXPath("./span[@itemprop='offers']"));
BigDecimal price = new BigDecimal(((HtmlElement) offers.getFirstByXPath("./span[@itemprop='price']")).asText());
String productName = (((HtmlElement) productNode.getFirstByXPath("./span[@itemprop='name']")).asText());
String currency = (((HtmlElement) offers.getFirstByXPath("./*[@itemprop='priceCurrency']")).getAttribute("content"));
String productSKU = (((HtmlElement) productNode.getFirstByXPath("./span[@itemprop='sku']")).asText());
在第一行中,我创建了HtmlUnit HTTP客户端并禁用了Javascript,因为我们不需要它来获取Schema标记。
然后,只是基本的XPath表达式即可选择我们想要的有趣的DOM节点。
该解析器远非完美,它不能提取所有内容,也不能处理多个要约。 但是,这将为您提供有关如何提取模式数据的想法。
然后,我们可以创建Product对象,并将其打印为JSON字符串:
Product product = new Product(price, productName, productSKU, imageUrl, currency);
ObjectMapper mapper = new ObjectMapper();
String jsonString = mapper.writeValueAsString(product) ;
System.out.println(jsonString);
避免阻塞
现在我们已经能够提取出我们想要的产品数据,我们必须小心不要被阻塞。
由于各种原因,有时会在网站上实施反机器人机制。 保护网站免受僵尸程序侵害的最明显的原因是,防止大量的自动流量影响网站的性能(并且您必须小心并发请求,因为它会增加延迟...)。 另一个原因是阻止垃圾邮件等僵尸程序的不良行为。
如今有各种各样的保护机制。有时你的机器人会被阻塞,如果它每秒钟/每小时/每天做太多的请求。有时每个IP地址的请求数是有速率限制的。最困难的保护是用户行为分析。例如,如果同一IP同时发出请求,则该网站可以分析请求之间的时间间隔。
隐藏我们的刮板最简单的解决方案是使用代理。 与随机用户代理程序结合使用时,使用代理服务器是一种强大的方法,可以隐藏我们的抓取工具,并抓取受速率限制的网页。 当然,最好不要一开始就阻止它,但有时网站每天/小时只允许一定数量的请求。
在这种情况下,您应该使用代理。 免费代理列表很多,我不建议您使用这些代理列表,因为它们通常速度慢,不可靠,而且提供这些列表的网站对于这些代理的位置并不总是透明的。 有时,公共代理人名单是由一家合法公司运营的,提供高级代理,有时则不...
我建议您使用付费代理服务,或者您可以自己构建。
为HtmlUnit设置代理很容易:
ProxyConfig proxyConfig = new ProxyConfig("host", myPort);
client.getOptions().setProxyConfig(proxyConfig);
进一步来说
如您所见,由于有了Schema.org数据,所以现在提取产品数据比十年前要容易得多。
但是仍然存在一些挑战,比如处理没有实现模式的网站、处理IP阻塞和速率限制、呈现Javascript……
这就是为什么我们一直与我的合作伙伴Pierre合作开发Web Scraping API的原因
ScrapingBee是一种API,可从任何网站提取任何HTML,而无需处理代理、验证码和无头浏览器。 单个API调用,仅包含您要从中提取数据的产品URL。
我希望你喜欢这篇文章,因为你总是可以在这个Github知识库中找到完整的代码:https://github.com/ksahin/introweb