bing每日一图适合作桌面背景,也经常作为我们网站某个页面的背景,效果非常好,下面我们来介绍Java如何爬取。
这次爬取介绍两个方法
方法一,爬取目标网页,通过网页元素得到图片路径
方法二,爬取图片url地址的接口,通过返回的json数据爬取图片
方法一:
-
分析: 首先我们打开bing首页
https://www.bing.com/?mkt=zh-CN
,按照正常的思路,我们右键检查,在页面上寻找目标图片,然后我们可以找到一个看起来很像背景图的:
我们复制出来/th?id=OHR.MatiSiTemple_ZH-CN1153907273_UHD.jpg&rf=LaDigue_UHD.jpg&pid=hp&w=1920&h=1080&rs=1&c=4
/代表使用的是相对路径,我们只需要拼接上网站根路径(https://www.bing.com)即可, 我们在浏览器输入,发现,这个就是我们想要的图片地址了。
但是,浏览器最终解析的可能和我们看到的不一样,因为存在很多js请求,所以我们打开network,清空,输入bing主页,查看请求的返回值。
如图,果然和我们在页面上看到的不一样,我们再尝试去寻找返回的数据中是否有我们想要的。(我们在页面中已经找到了图片的url,所以我们在返回值搜索url即可),我们就也在返回值中找到了图片的url,我们找到了两处,一个在一个id为bgImgProgLoad
的div中,另一个在id为bgLink
的link中,我们使用哪个都可以
分析至此,我们就已经捋清了要爬取的步骤了,首先发送Get请求访问bing首页,再使用Jsoup解析返回的页面,获取到id为bgLink的元素的url,然后再次发送Get请求图片,将图片下载即可。 -
编码 (pom文件我放到了最后)
因为有两种方法,所以我代码抽取到一个工具类中
- 工具类 HttpUtils
代码中打了部分注释,如果对哪部分觉得不清楚可以查看上一篇爬取豆瓣的博客,注释非常详细
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.util.EntityUtils;
import java.io.*;
import java.text.SimpleDateFormat;
import java.util.Date;
public class HttpUtils {
// 配置HttpClients连接池管理器,可以自动释放连接池资源
static PoolingHttpClientConnectionManager manager;
static {
// 为了方便我直接丢到静态代码块中初始化
manager = new PoolingHttpClientConnectionManager();
manager.setMaxTotal(100);
manager.setDefaultMaxPerRoute(10);
}
public static String doGetHtml(String url) {
// 创建httpclient对象用于发起请求
CloseableHttpClient httpClient = HttpClients.custom().setConnectionManager(manager).build();
// 创建get请求,指定uri
HttpGet httpGet = new HttpGet(url);
// 设置连接的一些参数,可以不设置
httpGet.setConfig(getConfig());
// 设置user-agent来欺骗网站是浏览器访问
httpGet.setHeader("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.75 Safari/537.36");
CloseableHttpResponse response = null;
try {
response = httpClient.execute(httpGet);
if (response.getStatusLine().getStatusCode() == 200) {
// 如果返回状态码为200代表成功,则将响应数据返回
return EntityUtils.toString(response.getEntity(), "utf8");
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (response != null) {
try {
// 这里只需要释放response,httpclient由连接池管理器释放
response.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return "";
}
/**
* 下载图片的方法基本与get请求类似
* 主要区别在我们不返回响应字符串,而是将响应数据直接通过字节流写入文件
*/
public static void doGetImage(String url) {
CloseableHttpClient httpClient = HttpClients.custom().setConnectionManager(manager).build();
HttpGet httpGet = new HttpGet(url);
httpGet.setHeader("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.75 Safari/537.36");
CloseableHttpResponse response = null;
try {
response = httpClient.execute(httpGet);
if (response.getStatusLine().getStatusCode() == 200) {
// 这里格式化下时间作为文件名
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss-S");
String dateStr = simpleDateFormat.format(new Date());
// 创建字节缓冲输出流,大家要先创建F:/img/bing文件夹,我这里没有进行判断
BufferedOutputStream bs = new BufferedOutputStream(new FileOutputStream("F:\\img\\bing\\" + dateStr + ".png"));
// 响应数据直接写入输出流中
response.getEntity().writeTo(bs);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (response != null) {
try {
response.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
private static RequestConfig getConfig() {
RequestConfig config = RequestConfig.custom().setConnectionRequestTimeout(2000)
.setConnectTimeout(3000)
.setSocketTimeout(10 * 1000)
.build();
return config;
}
}
- 测试
我们创建个Main进行测试下
import cn.jayjia.util.HttpUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.select.Elements;
public class Main {
public static void main(String[] args) {
// 方法1 抓取页面数据
String html = HttpUtils.doGetHtml("https://www.bing.com/?mkt=zh-CN");
Document document = Jsoup.parse(html);
Elements e = document.select("#bgLink");
System.out.println(e.attr("href"));
HttpUtils.doGetImage("https://www.bing.com" + e.attr("href"));
}
}
执行就可以下载成功了。
方法二
-
分析:
我们除了可以从网页拿到数据,那么能不能直接从接口拿到返回数据呢?这两种思路我们在爬虫的时候都应该考虑一下,因此,我们F12,打开network重新刷新网页,点击All观察所有请求,可以找到这样一条看起来很像的请求
我们点击进去查看reponse
发现这和上一个获取到的url是一样的,看来我们找到了想要的接口,请求这个地址返回的是JSON数据,所以我们可以使用JackSon进行解析,获取到url就能进行下载了。 -
编码
我们可以使用工具类中的doGetHtml获取返回的JSON数据,这就是封装工具类的好处了,解析完JSON数据后,再调用doGetImage方法下载即可,所以我们只需要进行解析JSON,那么就直接写测试吧
- 测试
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
public class Main {
public static void main(String[] args) {
// 方法2 抓取接口数据 https://www.bing.com/HPImageArchive.aspx?format=js&idx=0&n=1&nc=1603114218081&pid=hp&mkt=zh-CN
String json = HttpUtils.doGetHtml("https://www.bing.com/HPImageArchive.aspx?format=js&idx=0&n=1&nc=1603114218081&pid=hp&mkt=zh-CN");
// 这里解析JSON我用的Jackson,大家也可以使用别的如GSON等
ObjectMapper mapper = new ObjectMapper();
try {
JsonNode jsonNode = mapper.readTree(json);
String url = jsonNode.get("images").get(0).get("url").asText();
System.out.println(url);
HttpUtils.doGetImage("http://www.bing.com" + url);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
}
}
测试就可以下载成功了。
- POM文件依赖
<dependencies>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.7</version>
</dependency>
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.10.3</version>
</dependency>
<!--解析JSON-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.10.2</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.10.2</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.10.2</version>
</dependency>
</dependencies>