Java写爬虫,你试过嘛?1000本小说100行代码搞定

举报
山河已无恙 发表于 2021/10/20 21:29:46 2021/10/20
【摘要】 我徒然学会了抗拒热闹,却还来不及透悟真正的冷清。——张大春 《四喜忧国》
入手二手Kindle Voyage一部, 准备下几本书,知乎找到一个叫好读的网站,发现好多好书,繁体竖版,嘻嘻,下了几次,读着感觉棒棒的,所以,想把整个网站的书都爬下来。哈哈…, 而且分析网站,表格布局,SEO友好。
  • 初步定的方案用 python,因为IO读写方便,结合xpath,后来搭了环境,发现好多都忘记了,需要复习,所有最后决定用java,结合jsoup,htmlUtil等。
  • 关于 jsoup ,可以看我的博客:Jsoup学习文档
  • 捣鼓了一晚上,折腾到凌晨3、4点多,终于爬了下来。原本想一个页面下载小说的多个类型,后来发现做不到,一段代码并行跑的。
  • 爬取小说的网站
在这里插入图片描述

设计到技术点:

  • 需要模拟下载按钮的点击,还有之后弹出的确认框的按钮点击。这里的思路是调用两次按钮点击事件对应方法,第一次click返回page,获取按钮Element在调用一次返回的page直接输出为IO,
  • 按钮的多次点击之间,页面会通过js动态生成Element。如果两次点击事件串行触发,可能需要的Element数据没有加载出来,获取不到第二次的按钮元素。报NullPointException。这个处理是让线程sleep了一秒。确保js加载的Element可以加载出来。
  • 当前代码同一个页面不支持多次按钮点击下载,如果因为在一次下载完无法获取到当前页面了,所以不能并行操作,解决办法现在还没想到,小伙伴可以留言idea。
  • 剩下的需要注意一些版本依赖问题。
  • 默认的处理异常逻辑为,当前小说下载出现异常会直接跳过。

代码没有处理,需要优化的可以自行处理下
依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.liruilon</groupId>
    <artifactId>spider</artifactId>
    <version>1.0-SNAPSHOT</version>
    <dependencies>

        <!-- simulate web browser -->
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
            <version>4.5.7</version>
        </dependency>

        <!-- parse DOM -->
        <dependency>
            <groupId>org.jsoup</groupId>
            <artifactId>jsoup</artifactId>
            <version>1.11.3</version>
        </dependency>

        <!-- jackson -->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.9.8</version>
        </dependency>

        <!-- parse javascript -->
        <dependency>
            <groupId>org.mozilla</groupId>
            <artifactId>rhino</artifactId>
            <version>1.7.10</version>
        </dependency>

        <!-- simulate client action -->
        <dependency>
            <groupId>net.sourceforge.htmlunit</groupId>
            <artifactId>htmlunit</artifactId>
            <version>2.33</version>
        </dependency>

        <!-- upgrade junit to junit4 -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12<!-- default is v3.8.1 --></version>
            <scope>test</scope>
        </dependency>

        <!-- log -->
        <!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-api -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.25</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-log4j12 -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.25</version>
            <!-- <scope>test</scope> -->
        </dependency>
        <!-- https://mvnrepository.com/artifact/commons-beanutils/commons-beanutils -->
        <dependency>
            <groupId>commons-beanutils</groupId>
            <artifactId>commons-beanutils</artifactId>
            <version>1.9.4</version>
        </dependency>
        <!--commons-io-->
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.5</version>
        </dependency>

        <!--guava-->
        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>18.0</version>
        </dependency>

    </dependencies>

</project>

import com.gargoylesoftware.htmlunit.Page;
import com.gargoylesoftware.htmlunit.WebClient;
import com.gargoylesoftware.htmlunit.html.DomElement;
import com.gargoylesoftware.htmlunit.html.HtmlPage;
import com.google.common.collect.ImmutableMap;

import com.google.common.io.Files;
import org.apache.commons.io.FileUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

import java.io.*;

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;
import java.util.stream.Collectors;

import static org.apache.commons.io.IOUtils.copyLarge;

/**
 * @Description :
 * @Author: Liruilong
 * @Date: 2020/10/13 21:15
 */
public class Test {
    static Logger logger = Logger.getLogger(Test.class.getCanonicalName());
    static ExecutorService executorService = Executors.newFixedThreadPool(20);
    static final Map<String, String> mapTextStyle = ImmutableMap.of(
           // "下載 updb 檔", "updb"
            // "下載 prc 檔", "prc"
            "下載直式 mobi 檔", "mobi"
            //"下載 epub 檔", "epub"
    );

    /**
     * @param args
     * @return
     * @description
     * @author Liruilong
     * @date 2020年10月15日  03:10:12
     **/

    public static void main(String[] args) {


        String[] strings = new String[]{"http://www.haodoo.net/?M=hd&P=100", "http://www.haodoo.net/?M=hd&P=wisdom", "http://www.haodoo.net/?M=hd&P=history", "http://www.haodoo.net/?M=hd&P=martial", "http://www.haodoo.net/?M=hd&P=mystery",
                "http://www.haodoo.net/?M=hd&P=scifi", "http://www.haodoo.net/?M=hd&P=romance", "http://www.haodoo.net/?M=hd&P=fiction"};
        for (int j = 0; j < strings.length; j++) {
            try {
                Document doc = null;
                doc = Jsoup.connect(strings[j]).get();
                Elements s = doc.select("a[href]");
                logger.info("爬取:" + strings[j] + "__________---------——————————————" + Thread.currentThread().getName());
                List<Element> elements = s.stream().filter(a -> a.attr("abs:href").indexOf("book") != -1 && a.text().length() > 1)
                        .collect(Collectors.toList());
                executorService.execute(() -> {
                    for (int i = 0; i < elements.size(); i++) {
                        try {
                            WebClient webclient = new WebClient();
                            logger.info("爬取:" + elements.get(i).text() + "__________---------——————————————" + Thread.currentThread().getName());
                            HtmlPage htmlpage = null;
                            htmlpage = webclient.getPage(elements.get(i).attr("abs:href"));
                            List<DomElement> domElements = htmlpage.getElementsByTagName("input").stream().filter(o ->
                                    mapTextStyle.containsKey(o.getAttribute("value"))).collect(Collectors.toList());
                            for (int i1 = 0; i1 < domElements.size(); i1++) {
                                try {
                                    String textNameStyle = mapTextStyle.get(domElements.get(i1).getAttribute("value"));
                                    logger.info("爬取:" + elements.get(i).text() + "___" + textNameStyle + "_______---------——————————————" + Thread.currentThread().getName());
                                    HtmlPage page = domElements.get(i1).click();
                                    TimeUnit.SECONDS.sleep(1);
                                    DomElement button = page.getElementById("okButton");
                                    if (Objects.isNull(button)) {
                                        TimeUnit.SECONDS.sleep(1);
                                    }
                                    final InputStream inputStream = button.click().getWebResponse().getContentAsStream();
                                    saveFile(inputStream, elements.get(i).text(), textNameStyle);

                                } catch (Exception e) {
                                    continue;
                                }
                            }
                            webclient.close();//关掉
                        } catch (IOException e) {
                             continue;
                        }
                    }

                });
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }


    /**
     * @param io
     * @param s
     * @return 文件保存
     * @description
     * @author Liruilong
     * @date 2020年10月15日  00:10:06
     **/

    public static void saveFile(InputStream io, String... s) {
        executorService.execute(() -> {
            logger.info("-----------------------------------------------------------------------导入开始:" + s[0] + "__________---------——————————————" + Thread.currentThread().getName());
            try (OutputStream outputStream = new FileOutputStream(new File("G:\\codedemo\\src\\main\\" + s[0]
                    .replaceAll("【", "").replaceAll("】", "")
                    .replaceAll("《", "").replaceAll("》", "") + "." + s[1]));) {
                copyLarge(io, outputStream);
                outputStream.flush();
                logger.info("-------------------------------------------------------------------------导入结束:" + s[0] + "__________---------——————————————" + Thread.currentThread().getName());
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        });
    }
}

爬取日志

在这里插入图片描述

写入数据

在这里插入图片描述

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。