初步定的方案用 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();
}
});
}
}