请选择 进入手机版 | 继续访问电脑版
  • 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    公众号

TouTiaoCrawler: java spring+mybatis整合实现爬虫之《今日头条》搞笑动态图片爬取( ...

原作者: [db:作者] 来自: 网络 收藏 邀请

开源软件名称:

TouTiaoCrawler

开源软件地址:

https://gitee.com/z77z/TouTiaoCrawler

开源软件介绍:

java spring+mybatis整合实现爬虫之《今日头条》搞笑动态图片爬取(详细)


一.此爬虫介绍

今日头条本身就是做爬虫的,爬取各大网站的图片文字信息,再自己整合后推送给用户,特别是里面的动态图片,很有意思。在网上搜了搜,大多都是用Python来写的,本人是学习javaweb这块的,对正则表达式也不是很熟悉,就想着能不能换个我熟悉的方式来写。此爬虫使用spring+mybatis框架整合实现,使用mysql数据库保存爬取的数据,用jsoup来操作HTML的标签节点(完美避开正则表达式),获取文章中动态图片的链接,通过响应头中“Content-Type”的值来判断图片的格式,再将图片保存在本地。当然也可以爬取里面的文字,比如一些搞笑的黄段子,在此基础上稍加改动就可以实现,此爬虫只是提供一个入门的思路,更多好玩的爬虫玩法还待大家去开发,哈哈。

二.技术选型

  1. 核心语言:java;
  2. 核心框架:spring;
  3. 持久层框架:mybatis;
  4. 数据库连接池:Alibaba Drui;
  5. 日志管理:Log4j;
  6. jar包管理:maven; 。。。。

三.找规律,划重点

打开头条首页,找到点击搞笑模块,点击F12,下滚后加载下一页,发现是通过ajax请求api来获取的数据,如下图:

这里写图片描述

这是响应的json数据,里面的参数和值顾名思义大家都懂得。

这里写图片描述

是ajax访问就好解决了,通过我百度谷歌各种研究后发现,ajax请求的前三个参数是不变的,改变category参数是请求不同的模块,本列子是请求的搞笑模块所以值为funny,max_behot_time和max_behot_time_tmp这两个参数值是时间戳,首次请求是0,之后的值是响应json数据里面的next中的值。as和cp值是通过一段js生成的,其实就是一个加密了的时间戳而已。js代码后面会贴。

四.开始搭框架撸代码

项目搭建后之后为下图所示的文件结构,不懂得自行谷歌 哈哈

这里写图片描述

不多说直接上核心代码了:

package io.z77z.main;import io.z77z.dao.FunnyMapper;import io.z77z.entity.Funny;import java.io.BufferedInputStream;import java.io.BufferedReader;import java.io.FileOutputStream;import java.io.FileReader;import java.io.IOException;import java.io.InputStreamReader;import java.net.HttpURLConnection;import java.net.URL;import java.util.Date;import java.util.UUID;import javax.script.Invocable;import javax.script.ScriptEngine;import javax.script.ScriptEngineManager;import org.jsoup.Connection;import org.jsoup.Jsoup;import org.jsoup.nodes.Document;import org.jsoup.nodes.Element;import org.jsoup.select.Elements;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;import com.alibaba.fastjson.JSON;import com.alibaba.fastjson.JSONArray;import com.alibaba.fastjson.JSONObject;public class TouTiaoCrawler {	// 搞笑板块的api地址	public static final String FUNNY = "http://www.toutiao.com/api/pc/feed/?utm_source=toutiao&widen=1";	// 头条首页地址	public static final String TOUTIAO = "http://www.toutiao.com";	// 使用"spring.xml"和"spring-mybatis.xml"这两个配置文件创建Spring上下文	static ApplicationContext ac = new ClassPathXmlApplicationContext(			"spring-mybatis.xml");	// 从Spring容器中根据bean的id取出我们要使用的funnyMapper对象	static FunnyMapper funnyMapper = (FunnyMapper) ac.getBean("funnyMapper");	// 接口访问次数	private static int refreshCount = 0;	// 时间戳	private static long time = 0;	public static void main(String[] args) {		System.out.println("----------开始干活!-----------------");		while (true) {			crawler(time);		}	}	public static void crawler(long hottime) {// 传入时间戳,会获取这个时间戳的内容		refreshCount++;		System.out.println("----------第" + refreshCount + "次刷新------返回的请求时间为:"				+ hottime + "----------");		String url = FUNNY + "&max_behot_time=" + hottime				+ "&max_behot_time_tmp=" + hottime;		JSONObject param = getUrlParam(); // 获取用js代码得到的as和cp的值		// 定义接口访问的模块		/*		 * __all__ : 推荐 news_hot: 热点 funny:搞笑		 */		String module = "funny";		url += "&as=" + param.get("as") + "&cp=" + param.get("cp")				+ "&category=" + module;		JSONObject json = null;		try {			json = getReturnJson(url);// 获取json串		} catch (Exception e) {			e.printStackTrace();		}		if (json != null) {			time = json.getJSONObject("next").getLongValue("max_behot_time");			JSONArray data = json.getJSONArray("data");			for (int i = 0; i < data.size(); i++) {				try {					JSONObject obj = (JSONObject) data.get(i);					// 判断这条文章是否已经爬过					if (funnyMapper.selectByGroupId((String) obj							.get("group_id")) != null) {						System.out								.println("----------此文章已经爬过啦!-----------------");						continue;					}					// 访问页面返回document对象					String url1 = TOUTIAO + "/a" + obj.getString("group_id");					Document document = getArticleInfo(url1);					System.out.println("----------成功访问了文章:" + url1							+ "-----------------");					// 将document也存入					obj.put("document", document.toString());					// 将json对象转换成java Entity对象					Funny funny = JSON.parseObject(obj.toString(), Funny.class);					// json入库					funny.setBehotTime(new Date());					funnyMapper.insertSelective(funny);				} catch (Exception e) {					e.printStackTrace();				}			}		} else {			System.out.println("----------返回的json列表为空----------");		}	}	// 访问接口,返回json封装的数据格式	public static JSONObject getReturnJson(String url) {		try {			URL httpUrl = new URL(url);			BufferedReader in = new BufferedReader(new InputStreamReader(					httpUrl.openStream(), "UTF-8"));			String line = null;			String content = "";			while ((line = in.readLine()) != null) {				content += line;			}			in.close();			return JSONObject.parseObject(content);		} catch (Exception e) {			System.err.println("访问失败:" + url);			e.printStackTrace();		}		return null;	}	// 获取网站的document对象	public static Document getArticleInfo(String url) {		try {			Connection connect = Jsoup.connect(url);			Document document;			document = connect.get();			Elements article = document.getElementsByClass("article-content");			if (article.size() > 0) {				Elements a = article.get(0).getElementsByTag("img");				if (a.size() > 0) {					for (Element e : a) {						String url2 = e.attr("src");						// 下载img标签里面的图片到本地						saveToFile(url2);					}				}			}			return document;		} catch (IOException e) {			System.err.println("访问文章页失败:" + url + "  原因" + e.getMessage());			return null;		}	}	// 执行js获取as和cp参数值	public static JSONObject getUrlParam() {		JSONObject jsonObject = null;		FileReader reader = null;		try {			ScriptEngineManager manager = new ScriptEngineManager();			ScriptEngine engine = manager.getEngineByName("javascript");			String jsFileName = "toutiao.js"; // 读取js文件			reader = new FileReader(jsFileName); // 执行指定脚本			engine.eval(reader);			if (engine instanceof Invocable) {				Invocable invoke = (Invocable) engine;				Object obj = invoke.invokeFunction("getParam");				jsonObject = JSONObject.parseObject(obj != null ? obj						.toString() : null);			}		} catch (Exception e) {			e.printStackTrace();		} finally {			try {				if (reader != null) {					reader.close();				}			} catch (IOException e) {				e.printStackTrace();			}		}		return jsonObject;	}	// 通过url获取图片并保存在本地	public static void saveToFile(String destUrl) {		FileOutputStream fos = null;		BufferedInputStream bis = null;		HttpURLConnection httpUrl = null;		URL url = null;		String uuid = UUID.randomUUID().toString();		String fileAddress = "d:\\imag/" + uuid;// 存储本地文件地址		int BUFFER_SIZE = 1024;		byte[] buf = new byte[BUFFER_SIZE];		int size = 0;		try {			url = new URL(destUrl);			httpUrl = (HttpURLConnection) url.openConnection();			httpUrl.connect();			String Type = httpUrl.getHeaderField("Content-Type");			if (Type.equals("image/gif")) {				fileAddress += ".gif";			} else if (Type.equals("image/png")) {				fileAddress += ".png";			} else if (Type.equals("image/jpeg")) {				fileAddress += ".jpg";			} else {				System.err.println("未知图片格式");				return;			}			bis = new BufferedInputStream(httpUrl.getInputStream());			fos = new FileOutputStream(fileAddress);			while ((size = bis.read(buf)) != -1) {				fos.write(buf, 0, size);			}			fos.flush();			System.out.println("图片保存成功!地址:" + fileAddress);		} catch (IOException e) {			e.printStackTrace();		} catch (ClassCastException e) {			e.printStackTrace();		} finally {			try {				fos.close();				bis.close();				httpUrl.disconnect();			} catch (IOException e) {				e.printStackTrace();			} catch (NullPointerException e) {				e.printStackTrace();			}		}	}}

获取as和cp参数的js代码

function getParam(){    var asas;    var cpcp;    var t = Math.floor((new Date).getTime() / 1e3)      , e = t.toString(16).toUpperCase()      , i = md5(t).toString().toUpperCase();    if (8 != e.length){        asas = "479BB4B7254C150";        cpcp = "7E0AC8874BB0985";    }else{        for (var n = i.slice(0, 5), o = i.slice(-5), a = "", s = 0; 5 > s; s++){            a += n[s] + e[s];        }        for (var r = "", c = 0; 5 > c; c++){            r += e[c + 3] + o[c];        }        asas = "A1" + a + e.slice(-3);        cpcp= e.slice(0, 3) + r + "E1";    }    return '{"as":"'+asas+'","cp":"'+cpcp+'"}';}!function(e) {    "use strict";    function t(e, t) {        var n = (65535 & e) + (65535 & t)          , r = (e >> 16) + (t >> 16) + (n >> 16);        return r << 16 | 65535 & n    }    function n(e, t) {        return e << t | e >>> 32 - t    }    function r(e, r, o, i, a, u) {        return t(n(t(t(r, e), t(i, u)), a), o)    }    function o(e, t, n, o, i, a, u) {        return r(t & n | ~t & o, e, t, i, a, u)    }    function i(e, t, n, o, i, a, u) {        return r(t & o | n & ~o, e, t, i, a, u)    }    function a(e, t, n, o, i, a, u) {        return r(t ^ n ^ o, e, t, i, a, u)    }    function u(e, t, n, o, i, a, u) {        return r(n ^ (t | ~o), e, t, i, a, u)    }    function s(e, n) {        e[n >> 5] |= 128 << n % 32,        e[(n + 64 >>> 9 << 4) + 14] = n;        var r, s, c, l, f, p = 1732584193, d = -271733879, h = -1732584194, m = 271733878;        for (r = 0; r < e.length; r += 16)            s = p,            c = d,            l = h,            f = m,            p = o(p, d, h, m, e[r], 7, -680876936),            m = o(m, p, d, h, e[r + 1], 12, -389564586),            h = o(h, m, p, d, e[r + 2], 17, 606105819),            d = o(d, h, m, p, e[r +
                      

鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
热门推荐
热门话题
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap