好长时间没写博客了。
最近有个需求要一些房源平台的数据,看了看相关的网站数据再加上之前用Jsoup爬取过网站数据的经验觉得没什么问题。
于是用jsoup写了个简易demo进行某8数据爬取,没爬个几分钟呢就验证码了~
看来网站有反爬意识,毕竟靠数据资源吃饭的。 咦?浏览器看到的价位是正常的怎么爬取下来的html内容价位就是乱码呢?
因为自定义字体,每个详情页的字体都是动态加载的,关于加密字体解析的可以参考下面几个文章
https://www.cnblogs.com/a595452248/p/10800845.html
https://www.jianshu.com/p/a5d904c5d88e
仔细看了看html内容 发现在meta 标签的description里价位又写上正确的内容了,那我就不明白为什么要加密字体了(斜眼笑)
但猫眼就不一样了,所有数字都使用加密字体,无所谓是什么字体,把base64的内容下载下来存储成文件,在找出映射关系即可知道数字具体内容了。
这里可以参考 Python版的 字体反爬处理
https://blog.csdn.net/xing851483876/article/details/82928607
后来在搜索资料的途中发现了webmagic这个插件,用起来也相当方便
webmagic项目地址 https://github.com/code4craft/webmagic 中文文档 http://webmagic.io/
以csdn论坛爬取为例,我要爬取论坛列表页面的html存储至本地,还有列表中的每条帖子的详情html内容存储至本地
DemoProcessor
package com.personal.secondhand.processor;
import com.personal.secondhand.pipeline.FileInfoPipeline;
import com.personal.secondhand.pipeline.FilePagePipeline;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import us.codecraft.webmagic.Page;
import us.codecraft.webmagic.Site;
import us.codecraft.webmagic.Spider;
import us.codecraft.webmagic.processor.PageProcessor;
/**
* csdn论坛爬取demo
*/
public class DemoProcessor implements PageProcessor {
/**
* 加入一些F12 debug到的请求头信息组装
*/
private Site site = Site.me()
.addHeader("F12查到的头信息key", "信息值")
.setUserAgent("伪装的useragent")
.setSleepTime(3000)
.setRetryTimes(3)
.setCycleRetryTimes(3);
@Override
public void process(Page page) {
String html = page.getHtml().get();
String url = page.getUrl().get();
if (url.indexOf("page") != -1) {
System.out.println("列表页");
// 使用jsoup 解析html内容 并分析出每个帖子的详情链接
Document document = Jsoup.parse(html);
Elements elements = document.select("a[class=forums_title]");
for (Element ele : elements) {
String infoUrl = ele.attr("href");
// 将列表页的详情url添加至任务中继续处理
page.addTargetRequest(infoUrl);
}
// 将获取到的列表页html内容交由FilePagePipeline数据处理里
page.putField("pageHtml",html);
} else {
System.out.println("详情页");
// 将获取到的详情页html内容交由FileInfoPipeline数据处理里
page.putField("infoHtml",html);
}
}
public static void main(String[] args) {
// 创建一个任务 处理
Spider spider = Spider.create(new DemoProcessor());
// 多个任务url
spider.addUrl("https://bbs.csdn.net/forums/J2EE?page=1");
spider.addUrl("https://bbs.csdn.net/forums/J2EE?page=2");
// 将页面解析后的数据交给FileInfoPipeline/FilePagePipeline处理
spider.addPipeline(new FileInfoPipeline("d:/csdnhtml/"));
spider.addPipeline(new FilePagePipeline("d:/csdnhtml/"));
// 开启多个线程
spider.thread(4);
// 启动
spider.run();
}
@Override
public Site getSite() {
return site;
}
}
FileInfoPipeline
package com.personal.secondhand.pipeline;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTime;
import us.codecraft.webmagic.ResultItems;
import us.codecraft.webmagic.Task;
import us.codecraft.webmagic.pipeline.FilePipeline;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
/**
* 公共的详情页面下载到本地
* 只要接收到infoHtml即生成列表html页面
* 文件命名替换(原命名可能无法找到具体访问url):
* (1)去除https://
* (2)?替换成#
* (3)/替换成_
*/
@Slf4j
public class FileInfoPipeline extends FilePipeline {
public FileInfoPipeline() {
super();
}
public FileInfoPipeline(String path) {
super(path);
}
@Override
public void process(ResultItems resultItems, Task task) {
// 从PageProcess获取设置的html内容
String html = resultItems.get("infoHtml");
if (StringUtils.isBlank(html)) {
// 没有就跳出
return;
}
String url = resultItems.getRequest().getUrl();
url = url.replaceAll("https://", "").replaceAll("\\?", "#").replaceAll("/", "_");
// 文件命名为url路径,替换文件命名不符合的情况后形式如:
// 以下内容参考至FilePipeline#process 替换了写入内容
String today = new DateTime().toString("yyyyMMdd");
String path = super.path + PATH_SEPERATOR + today + PATH_SEPERATOR + "infoHtml" + PATH_SEPERATOR + url;
try {
// PrintWriter printWriter = new PrintWriter(new OutputStreamWriter(new FileOutputStream(this.getFile(path + DigestUtils.md5Hex(resultItems.getRequest().getUrl()) + ".html")), "UTF-8"));
PrintWriter printWriter = new PrintWriter(new OutputStreamWriter(new FileOutputStream(this.getFile(path + ".html")), "UTF-8"));
printWriter.println(html);
printWriter.close();
} catch (IOException e) {
log.error("info html文件写入异常", e);
}
}
}
FilePagePipeline
package com.personal.secondhand.pipeline;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTime;
import us.codecraft.webmagic.ResultItems;
import us.codecraft.webmagic.Task;
import us.codecraft.webmagic.pipeline.FilePipeline;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
/**
* 公共列表页面html存储到本地
* 只要接收到pageHtml即生成列表html页面
* 文件命名替换(原命名可能无法找到具体访问url):
* (1)去除https://
* (2)?替换成#
* (3)/替换成_
*/
@Slf4j
public class FilePagePipeline extends FilePipeline {
public FilePagePipeline() {
super();
}
public FilePagePipeline(String path) {
super(path);
}
@Override
public void process(ResultItems resultItems, Task task) {
// 从PageProcess获取设置的html内容
String html = resultItems.get("pageHtml");
if (StringUtils.isBlank(html)) {
// 没有就跳出
return;
}
String url = resultItems.getRequest().getUrl();
url = url.replaceAll("https://", "").replaceAll("\\?", "#").replaceAll("/", "_");
// 以下内容参考至FilePipeline#process 替换了写入内容
String today = new DateTime().toString("yyyyMMdd");
String path = super.path + PATH_SEPERATOR + today + PATH_SEPERATOR + "pageHtml" + PATH_SEPERATOR + url;
try {
// PrintWriter printWriter = new PrintWriter(new OutputStreamWriter(new FileOutputStream(this.getFile(path + DigestUtils.md5Hex(resultItems.getRequest().getUrl()) + ".html")), "UTF-8"));
PrintWriter printWriter = new PrintWriter(new OutputStreamWriter(new FileOutputStream(this.getFile(path + ".html")), "UTF-8"));
printWriter.println(html);
printWriter.close();
} catch (IOException e) {
log.error("page html文件写入异常", e);
}
}
}
执行main方法即可,在D盘的csdnhtml文件夹就出现了相关文件 打开是需要的html内容,将html文件用jsoup解析内容即可。
这样一个简单的数据爬取就完成了。
在Demo的process方法中,判断列表任务还是详情任务后解析html内容发给待处理的FileInfoPipeline和FilePagePipeline
对应的key才会去处理。官方demo有很挺多例子可以参考。
另外还有的网站是js动态链接跳转的页面,你无法使用jsoup或webmagic直接访问返回正确的html内容,如芒果房源
这时候需要selenium 和 phantomjs结合去处理了(phantomjs不在维护了,最新版的selenium 也不支持phantomjs了,如需替换可以使用Chrome或Firefox的headless)
代码在下面项目地址中
Jsoup就不多介绍了,主要是简单url访问及解析页面document。【也使用过过滤xss,因为踩坑印象挺深,当初某个版本(1.7.1?)解析过滤把一个正常字符串如(&orderby=xx)吃了部分并改变了一个字符串类似乱码(乱码字符+by)形式,导致参数错误。升级最新版就好了,但方法也改变了一些】
jsoup的解析语法大家可以自行搜索。
附上项目地址:https://github.com/UncleY/secondhand
另外各大网站对爬虫的限制都在根目录下的robots.txt声明,关于爬虫协议搜索引擎讲的比我详细。提一下是让大家尽量少消耗对方服务器资源。
先到这里~