Jsoup+WebMagic+Selenium+phantomJs简易爬取房源信息网站内容并解析

好长时间没写博客了。

最近有个需求要一些房源平台的数据,看了看相关的网站数据再加上之前用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声明,关于爬虫协议搜索引擎讲的比我详细。提一下是让大家尽量少消耗对方服务器资源。

先到这里~

 

 

 

 

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要使用Jsoup获取图片,可以按照以下步骤进行操作: 1. 使用Jsoup.connect()方法连接到指定的URL。 2. 使用Jsoup.parse()方法解析HTML文档。 3. 使用doc.select()方法选择包含图片的元素。 4. 使用element.attr()方法获取图片的URL。 5. 使用Jsoup.connect()方法连接到图片的URL。 6. 使用Response.bodyAsBytes()方法获取图片的字节数组。 下面是一个示例代码,可以获取指定网页中的所有图片: ```java import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; import org.jsoup.select.Elements; import org.jsoup.Connection.Response; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; public class ImageDownloader { public static void main(String[] args) throws IOException { String url = "https://www.example.com"; Document doc = Jsoup.connect(url).get(); Elements imgs = doc.select("img[src~=(?i)\\.(png|jpe?g|gif)]"); for (Element img : imgs) { String imgUrl = img.attr("abs:src"); Response resultImageResponse = Jsoup.connect(imgUrl) .ignoreContentType(true).execute(); InputStream inputStream = resultImageResponse.bodyStream(); OutputStream outputStream = new FileOutputStream( "image_" + System.currentTimeMillis() + ".jpg"); int bytesRead = -1; byte[] buffer = new byte[4096]; while ((bytesRead = inputStream.read(buffer)) != -1) { outputStream.write(buffer, 0, bytesRead); } outputStream.close(); inputStream.close(); } } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值