背景描述:
前段时间,我哥让我给侄女想一个好名字,作为一个工科生,这倒有点难倒我了。隐约记得以前刷头条的时候刷到过一些好听的名字,于是便去搜了一下。这一搜不要紧,接下来给我推送的都是关于这方面的文章,而我就毫不客气地全都收藏了。刚好这两天闲下来了,便想着把保存下来。但一看,都收藏了几十篇文章,且都是图片,如果一篇篇地去看,然后下载,这无疑就是搬砖。这让我这种懒人无疑是不能接受了。于是便想着写一个爬虫吧,把全部都爬下来。
当然,有的砖必须得搬,比如各个文章的URL地址,这个以目前的能力还不能智能获取。首先手动获取了各篇文章的URL地址,如下图所示。
在得到文章的URL之后,便开始文章中各个图片爬取工作了。话不多说,直接放码。
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import org.apache.commons.io.FileUtils;
import cn.cxd.tools.FileTools;
public class DownLoadToutiaoPicture {
public static void main(String[] args) throws Exception {
String srcPath = "D:/name/url.txt";// 保存了各个文章URL地址
String dstPath = "D:/name/content.txt";// 保存了文章中各个图片的URL
String dstDir = "D:/name/";// 最终图片存放的目录
File srcFile = new File(srcPath);
File dstFile = new File(dstPath);
BufferedReader br = new BufferedReader(new FileReader(srcFile));
BufferedWriter bw = new BufferedWriter(new FileWriter(dstFile));
String line = null;
while ((null) != (line = br.readLine())) {
getContent(bw, line);
}
line = null;
BufferedReader br1 = new BufferedReader(new FileReader(dstFile));
ArrayList<String> totalPicUrlList = new ArrayList<>();// 存放各个图片的URL
while ((null) != (line = br1.readLine())) {
ArrayList<String> tmpList = parse(line);// 一篇文章中图片的URL地址。
totalPicUrlList.addAll(tmpList);// 加入到总list里面。
}
for (int index = 0; index < totalPicUrlList.size(); index++) {
// 使用了Apache第三方的common io,既然别人把轮子都造好了,那就发扬拿来主义吧!哈哈哈。。。
FileUtils.copyURLToFile(new URL(totalPicUrlList.get(index)), new File(dstDir + index + ".jpg"));
}
FileTools.close(br1, bw, br);
}
/**
* 根据文章的URL地址获取各个图片的URL。通过分析网页的源码可知,一个图片的URL完整地址可能分行显示。而全部图片的URL都包含在articleInfo里面,
* 因此,本文的方案是把articleInfo里面的内容全部获取到本地,然后再解析,即后面的parse()函数。
*
* @param bw
* @param line
* 文章的URL地址链接
* @throws Exception
*/
private static void getContent(BufferedWriter bw, String line) throws Exception {
URL url = new URL(line);
// 头条做了反爬机制,必须将程序伪装成浏览器访问才行。
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setRequestProperty("user-agent",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36");
BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream(), "UTF-8"));
// 将articleInfo里面的内容组装成一行,方便后面的解析工作。
StringBuilder sb = new StringBuilder();
boolean flag = false;
// 这里建议不要用while((null) !=
// (line=br.readLine()))来做,极有可能还没有读到articleInfo时就为空,导致程序退出。
for (int i = 0; i < 2000; i++) {
String input = br.readLine();
if (null != input) {
System.out.println(input);
if (input.contains("articleInfo")) {
System.out.println(input);
flag = true;
}
if (flag == true) {
sb.append(input);
System.out.println(input);
}
if (input.contains("groupId")) {// groupId为articleInfo结束后的第一行内容,读到这表示articleInfo内容已经读完,则将i置为200以退出循环。
i = 2000;
}
}
}
bw.write(sb.toString());
bw.newLine();
sb = new StringBuilder();
FileTools.close(br);
}
/**
* 解析出里面的URL地址,存入list并返回。
*
* @param line
* 上面提到的articleInfo里面的内容。
* @return
*/
private static ArrayList<String> parse(String line) {
int len = line.length();
char[] ch = line.toCharArray();
StringBuilder sb = new StringBuilder();
ArrayList<String> picUrlList = new ArrayList<>();
for (int i = 0; i < len - 4; i++) {
String tmp = line.substring(i, i + 4);
if (tmp.equals("http")) {
for (int j = i; j < len; j++) {
if (ch[j] != '&') {
sb.append(ch[j]);
} else {
j = len;
}
}
picUrlList.add(sb.toString());
sb = new StringBuilder();
}
}
return picUrlList;
}
}
关于遇到的坑已经在代码注释中做了说明。
另外,在程序最后也出现了异常,如下图所示:
这个还得日后再去深扒。
最后爬取的结果如下图所示: