java从网页抓取数据( 如何通过WebCollector抓取到内容,?, )

优采云 发布时间: 2022-04-06 14:36

  java从网页抓取数据(

如何通过WebCollector抓取到内容,?,

)

  

  前言

  上一篇文章我们简单讲了如何通过WebCollector来抓取内容,但这不符合我们的工作需要。在工作过程中,我们通常会抓取某个网页列表下的详情页数据,所以不能简单的从某个列表页抓取数据,需要跳转到详情页进行二次抓取数据. 好了,废话不多说,我们先从代码开始讲解如何操作。

  爬取列表信息

  假设我们抓取搜东首页显示文章的所有细节。如下图所示。

  

  第一步,我们不忙于创建爬虫,我们先分析一下我们需要爬取的网站的结构。根据我们需要的网页的URL地址的特点,写出正确的正则表达式。如下所示。

  http://www.jianshu.com/p/700e01a938ce

  我编写的匹配 文章 URL 的正则表达式如下所示。

  "http://www.jianshu.com/p/.*"

  第二步,正则表达式写好后,我们需要分析一下我们抓取到的网站的结构和标签。以谷歌浏览器为例。打开开发者模式的控制台。(Mac 常用的 F12 键 ++ alt +i,这里不再赘述),假设我们需要抓取 文章 的标题和内容。我们首先选择“选择工具”(快捷键:command +shift +c),然后点击标题,此时控制台中会出现标签信息。如下所示。

  

  我们发现title的标签h1的Class就是title,如下图。

  

  标题已经准备好了。接下来,我们来看看内容。经过以上步骤,我们发现内容在很多标签中。这时候我们只需要获取父类标签中的所有内容即可。

  

  对了,首页的列表页也是如上做的。如下图,所有的超链接都是一个标签。

  

  第三步,我们还是创建一个爬虫类JianshuCrawler,继承自AbstractCrawler,然后在初始化方法中配置我们的正则表达式和我们需要爬取的网站*敏*感*词*。其他一些配置如下。

   private final static String crawlPath = "/Users/luying/data/db/sports";

private final static String seed = "http://www.jianshu.com/u/e39da354ce50";

private final static String regexRuleString = "http://www.jianshu.com/p/.*";

public JianshuCrawler() {

super(crawlPath, false);

CrawlDatum datum = new CrawlDatum(seed).meta("depth", "2");

addSeed(datum);

this.addRegex(regexRuleString);

setThreads(2);

}

  在访问方法中,我们需要做两种处理,一种是爬取文章列表,另一种是爬取文章详情页的内容。所以我们需要用详情页URL的正则表达式来区分文章详情页和列表首页,结构如下。

   @Override

public void visit(Page page, CrawlDatums next) {

if (page.matchUrl(regexRuleString)) {

//详情页会进入这个模块

} else {

//列表首页会进入这个模块

}

}

  通过第二步的分析,我们知道列表页需要将所有超链接符合正则表达式的a标签添加到爬取序列中。具体操作如下。

   Elements aBodys = page.select("a");

for (int i = 0; i < aBodys.size(); i++) {

Element aElement = aBodys.get(i);

logger.debug("url=" + aElement.attr("abs:href"));

String regEx = regexRuleString;

if (aElement.attr("abs:href").matches(regEx)) {

CrawlDatum datum = new CrawlDatum(aElement.attr("abs:href")).meta("depth", "1").meta("refer",

page.url());

next.add(datum);

} else {

System.out.println("URL不匹配!!");

}

}

  对于详情页的逻辑,我们需要抓取对应元素的内容,可以使用类名(表单示例:div.xxx)或id名(表单示例:div[id = xxx]),这里没有id,所以我们直接使用类名,代码如下。

  

String title = page.select("h1.title").text();

String content = page.select("div.show-content").text();

  我们创建一个 main 函数来创建我们的爬虫对象。然后,我们需要设置爬取深度,因为我们只需要跳转到页面一次,所以我们的爬取深度是 2. 最后运行这个类。如下图所示。

   public static void main(String[] args) {

JianshuCrawler crawler = new JianshuCrawler();

crawler.start(2);

}

  这样,我们就会在控制台中得到我们想要的数据。当然,对数据的处理,这里就不过多解释了。如下图所示。

  

  爬虫类的完整代码如下所示。

  public class JianshuCrawler extends AbstractCrawler {

private static Logger logger = LoggerFactory.getLogger(JianshuCrawler.class);

private final static String crawlPath = "/Users/luying/data/db/sports";

private final static String seed = "http://www.jianshu.com/u/e39da354ce50";

private final static String regexRuleString = "http://www.jianshu.com/p/.*";

public JianshuCrawler() {

super(crawlPath, false);

CrawlDatum datum = new CrawlDatum(seed).meta("depth", "2");

addSeed(datum);

this.addRegex(regexRuleString);

setThreads(2);

}

@Override

public void visit(Page page, CrawlDatums next) {

if (page.matchUrl(regexRuleString)) {

String title = page.select("h1.title").text();

String content = page.select("div.show-content").text();

System.out.println("标题 " + title);

System.out.println("内容 " + content);

} else {

Elements aBodys = page.select("a");

for (int i = 0; i < aBodys.size(); i++) {

Element aElement = aBodys.get(i);

logger.debug("url=" + aElement.attr("abs:href"));

String regEx = regexRuleString;

if (aElement.attr("abs:href").matches(regEx)) {

CrawlDatum datum = new CrawlDatum(aElement.attr("abs:href")).meta("depth", "1").meta("refer",

page.url());

next.add(datum);

} else {

System.out.println("URL不匹配!!");

}

}

}

}

public static void main(String[] args) {

JianshuCrawler crawler = new JianshuCrawler();

crawler.start(2);

}

}

  抓取图像信息

  刮图片更简单。假设我们仍然爬取搜东首页中所有显示的图片。如下所示。

  

  爬虫类的基本步骤就不过多解释了。先说一下访问方面的处理。我们需要网页中标签的属性来判断。只有html属性和图片属性的标签可以是图片标签。所以我们首先得到如下图的标签名。

   String contentType = page.response().contentType();

  然后判断标签中收录的名称。如下所示执行不同的操作。

   if (contentType == null) {

return;

} else if (contentType.contains("html")) {

// 如果是网页,则抽取其中包含图片的URL,放入后续任务

} else if (contentType.startsWith("image")) {

// 如果是图片,直接下载

}

  标签是网页属性的处理,我们需要把里面的图片地址链接取出来,为接下来的处理做准备。代码如下。

   Elements imgs = page.select("img[src]");

for (Element img : imgs) {

String imgSrc = img.attr("abs:src");

next.add(imgSrc);

}

  如果是图片标签,我们可以直接取图片的字节数据存储。代码如下。

   String extensionName = contentType.split("/")[1];

String imageFileName = imageId.incrementAndGet() + "." + extensionName;

File imageFile = new File(downloadDir, imageFileName);

try {

FileUtils.write(imageFile, page.content());

System.out.println("保存图片 " + page.url() + " 到 " + imageFile.getAbsolutePath());

} catch (IOException ex) {

throw new RuntimeException(ex);

}

  因为,标签类型可能是网页,也就是说,我们需要跳转到下一页。这时候我们在main函数中创建了一个对象,需要设置爬取深度为2.

   public static void main(String[] args) throws Exception {

JianshuImageCrawler crawler = new JianshuImageCrawler();

crawler.start(2);

}

  图片的存储过程就不过多解释了。通过运行,我们可以在对应的存储路径下找到我们的图片。如下所示。

  

  整体代码如下所示。

  package com.infosports.yuqingmanagement.crawler.impl;

import java.io.File;

import java.io.IOException;

import java.util.concurrent.atomic.AtomicInteger;

import org.jsoup.nodes.Element;

import org.jsoup.select.Elements;

import cn.edu.hfut.dmic.webcollector.model.CrawlDatum;

import cn.edu.hfut.dmic.webcollector.model.CrawlDatums;

import cn.edu.hfut.dmic.webcollector.model.Page;

import cn.edu.hfut.dmic.webcollector.plugin.berkeley.BreadthCrawler;

import cn.edu.hfut.dmic.webcollector.util.FileUtils;

import cn.edu.hfut.dmic.webcollector.util.RegexRule;

public class JianshuImageCrawler extends BreadthCrawler {

// 用于保存图片的文件夹

File downloadDir;

// 原子性int,用于生成图片文件名

AtomicInteger imageId;

private final static String crawlPath = "/Users/luying/data/db/jianshu";

private final static String downPath = "/Users/luying/data/db/jianshuImage";

private final static String seed = "http://www.jianshu.com/u/e39da354ce50";

RegexRule regexRule = new RegexRule();

public JianshuImageCrawler() {

super(crawlPath, false);

downloadDir = new File(downPath);

if (!downloadDir.exists()) {

downloadDir.mkdirs();

}

computeImageId();

CrawlDatum datum = new CrawlDatum(seed).meta("depth", "2");

addSeed(datum);

regexRule.addRule("http://.*");

}

@Override

public void visit(Page page, CrawlDatums next) {

String contentType = page.response().contentType();

if (contentType == null) {

return;

} else if (contentType.contains("html")) {

// 如果是网页,则抽取其中包含图片的URL,放入后续任务

Elements imgs = page.select("img[src]");

for (Element img : imgs) {

String imgSrc = img.attr("abs:src");

next.add(imgSrc);

}

} else if (contentType.startsWith("image")) {

// 如果是图片,直接下载

String extensionName = contentType.split("/")[1];

String imageFileName = imageId.incrementAndGet() + "." + extensionName;

File imageFile = new File(downloadDir, imageFileName);

try {

FileUtils.write(imageFile, page.content());

System.out.println("保存图片 " + page.url() + " 到 " + imageFile.getAbsolutePath());

} catch (IOException ex) {

throw new RuntimeException(ex);

}

}

}

public void computeImageId() {

int maxId = -1;

for (File imageFile : downloadDir.listFiles()) {

String fileName = imageFile.getName();

String idStr = fileName.split("\\.")[0];

int id = Integer.valueOf(idStr);

if (id > maxId) {

maxId = id;

}

}

imageId = new AtomicInteger(maxId);

}

public static void main(String[] args) throws Exception {

// TODO Auto-generated method stub

JianshuImageCrawler crawler = new JianshuImageCrawler();

crawler.start(2);

}

  总结

  本篇博客到此结束,但是分享技术点的过程还没有结束。当然,毕竟Java刚入门不久,所以文章很多概念可能有歧义,如有错误,欢迎指导批评,万分感谢。

  

0 个评论

要回复文章请先登录注册


官方客服QQ群

微信人工客服

QQ人工客服


线