由于百度图片采用异步加载的方式,因此不能通过静态标签<img src>匹配找到图片的url,可考虑通过向服务器发送ajax请求的方式下载图片。
打开360浏览器或Chrom,按F12,切换到Network标签,然后将网页向下拉,观察发现地址栏的网址并没有发生变化,而图片在增加。
可以发现在下拉过程中会不断出现avatarjson请求,点开请求头信息,分析两个请求URL:
http://image.baidu.com/search/avatarjson?tn=resultjsonavatarnew&ie=utf-8&word=%E5%AE%8B%E4%BD%B3&cg=star&pn=90&rn=30&itg=0&z=0&fr=&width=&height=&lm=-1&ic=0&s=0&st=-1&gsm=5a
变化的地方如下:
word:搜索关键字,要用URL Encode,即非字母字符被替换成%后加两位十六进制数;
pn:每次递增30,每次异步请求可加载30张图片;
gsm: pn的十六进制值;
因此每次请求我们只需改变pn及gsm的值即可,用page对请求页面计数,初次请求时page为0,以后每次请求page加1。
将keyword进行URL Encode编码后,可以拼接出page次请求的URL:
http://image.baidu.com/search/avatarjson?tn=resultjsonavatarnew&ie=utf-8&word="+keyword+"&cg=star&pn="+page*30+"&rn=30&itg=0&z=0&fr=&width=&height=&lm=-1&ic=0&s=0&st=-1&gsm="+Integer.toHexString(page*30)
请求返回JSON数据,点击preview标签,找到imgs
发现一张图片有4种URL:fromURL,middleURL,thumbURL,objURL,前三种有反爬措施,因此采用objURL.
因此得到图片爬取的流程:
1)将搜索关键字keyword编码后得到请求地址;
2)得到该请求地址的JSON数据;
3)分析JSON数据匹配出objURL地址;
4)请求此地址下载图片。
多线程实现
为了加快爬取的速度,采用多线程设计。 各个线程同时下载各自请求得到的图片,因此要确保每个线程每次请求时的page不同,因此为page操作要实现同步。
程序如下:
import java.util.List;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.util.ArrayList;
/**
* @author Administrator
* 爬取百度图片,百度图片加载方式:异步加载,瀑布流式
*/
public class Crawer
{
private static int page=-1;
private static final String ENCODE="UTF-8";//编码
private static Lock lock=new ReentrantLock();
/**
* 将中文转换成URL编码
* @param keyword
*/
public static String encode(String keyword) throws UnsupportedEncodingException
{
return new String(URLEncoder.encode(keyword,ENCODE).getBytes());
}
public static void main(String[] args) throws UnsupportedEncodingException
{
String name="孙艺珍";
String downloadPath="F:\\downloadPic\\"+name+"\\";
String keyword=encode(name);
for(int i=0;i<8;i++)
{
new Thread("Thread--"+i){
public void run()
{
downloadPic(keyword,downloadPath);
}
}.start();
}
}
public static void downloadPic(String keyword,String downloadPath)
{
File path=new File(downloadPath);
if(!path.exists())
path.mkdirs();
while(!Thread.interrupted())
{
lock.lock();
try{
page++;
}finally{
lock.unlock();
}
String url="http://image.baidu.com/search/avatarjson?tn=resultjsonavatarnew&ie=utf-8&word="+keyword+"&cg=star&pn="+page*30+"&rn=30&itg=0&z=0&fr=&width=&height=&lm=-1&ic=0&s=0&st=-1&gsm="+Integer.toHexString(page*30);
String html=null;
try {
html=getHtml(url);
List<String> listUrl=getImgeUrl(html);
download(listUrl,downloadPath);
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* 获取url页面的内容
* @param url
* @return
* @throws Exception
*/
public static String getHtml(String url) throws Exception
{
URL uri=new URL(url);
StringBuffer sb=new StringBuffer();
URLConnection connection=uri.openConnection();
InputStream in=connection.getInputStream();
byte[] buf=new byte[1024];
int length=0;
while((length=in.read(buf,0,buf.length))>0)
sb.append(new String(buf,ENCODE));
in.close();
return sb.toString();
}
/**
* 获取图片URL
* @param html
* @return
*/
public static List<String> getImgeUrl(String html)
{
List<String> listUrl=new ArrayList<String>();
String reg = "objURL\":\"http://.+?\\.jpg";
Matcher matcher=Pattern.compile(reg).matcher(html);
while(matcher.find())
listUrl.add(matcher.group().substring(9));
return listUrl;
}
public static void download(List<String> listUrl,String downloadPath)
{
for(String url:listUrl)
{
String imageName=url.substring(url.lastIndexOf("/")+1,url.length());
try {
URL uri=new URL(url);
//URLConnection connection=uri.openConnection();
//HttpURLConnection httpCon=(HttpURLConnection)connection;
//InputStream in=httpCon.getInputStream();
InputStream in=uri.openStream();
String file=downloadPath+imageName;
FileOutputStream fo=new FileOutputStream(new File(file));
byte[] buf=new byte[1024];
int length=0;
while((length=in.read(buf,0,buf.length))!=-1)
fo.write(buf, 0, length);
System.out.println(Thread.currentThread().getName()+url+"下载完成!");
in.close();
fo.close();
} catch (FileNotFoundException e1) {
System.out.println("无法下载图片!");
} catch(IOException e2) {
System.out.println("发生IO异常!");
}
}
}
}
运行效果: