前言
前面已经分享过了,使用HttpClient模拟登陆今日头条,并且查看文章列表和发布纯文字类型的文章,今天会把HttpClient模拟发布图集和视频类文章在这里做一个分享。Java HttpClient模拟今日头条登陆的,可以参考以前发布的文章(HttpClient技术)(今日头条系列)HttpClient模拟今日头条登录,好了,话不多说,开发分享如何发布带图片的文章。
可以看到,今日头条发布的文章,里面可以带图片的,图片分为2种,1种是作为封面的图片,另外1种是文章内部的图片,其实这2种图片,在后台发布和模拟的方式,也是有差异的,话不多说,开始抓包分析。
分析及代码
根据抓包工具分析可以看到,今日头条在发布文章的时候,整体发布调用的接口是
https://mp.toutiao.com/core/article/edit_article_post/?source=mp&type=figure,发布的body体,是用JSON形式传输的,里面会拼接封面的图片,文章的正文,已经文章内的图片,但是图片,并不是按流的方式直接拼接到body体里面的,是在文章上传图片的时候,直接就会把图片上传到头条的后台,同时后台会返回一个JSON形式的结果,这个结果里面会包含上传成功的图片的链接,gallery_id,URI等信息,我们只需要把这个结果拿取到,拼接到body体中就可以实现模拟发布文章的功能了。
基本过程中,主要的部分是模拟图片上传成功,并且拿到头条返回的结果JSON串,和模拟调用edit_article_post发布文章的两个环节,重要信息的代码如下(关键代码):
上传图片代码
System.out.println("开始上传上传图片...") ; String original[] = new String[photopath.length] ; for(int i =0 ; i< photopath.length ; i++){ String sphotopath = photopath[i][0] ; File updateFileImg = new File(sphotopath) ; while(true){ System.out.println("上传图片:" + sphotopath) ; g3 = new HttpPost("https://mp.toutiao.com/tools/upload_picture/ ?type=ueditor&pgc_watermark=1&action=uploadimage&encode=utf-8") ; //这里不需要设置header,开始有设置,发现用处不大 // g3.setHeader("Accept", "*/*") ; // g3.setHeader("Content-Type", "application/x-www-form-urlencoded") ; //把图片作为文件,放到FileBody中,注意编码格式,这里用的是UTF-8 FileBody bin = new FileBody(updateFileImg, updateFileImg.getName(), "image/png", "UTF-8"); MultipartEntity reqEntity = new MultipartEntity(); //图片的描述,名称等属性 String keys = "Content-Disposition: form-data; name=\"upfile\"; filename=\"QQ截图20160801161100.png\"\r\nContent-Type: image/png" ; reqEntity.addPart("upfile" , bin);//file1为请求后台的File upload;属性 g3.setEntity(reqEntity); response2 = httpClient.execute(g3); //httpClient.executeMethod(g1) ; sg1 = EntityUtils.toString(response2.getEntity(),"utf-8") ; sg1 = StringRandomUtils.unicodeToString(sg1) ; System.out.println("上传图片:"+sg1) ; //如果state是SUCCESS就代表发布成功 if(response2.getStatusLine().getStatusCode() == 200 && sg1.indexOf("state\": \"SUCCESS")!=-1 && sg1.indexOf("original\": \"")!=-1 ){ //original字段是图片发布成功后,在头条后台的id,很重要 sg1 = sg1.substring(sg1.indexOf("original\": \"")+12) ; original[i] = sg1.substring(0, sg1.indexOf("\"")) ; break ; } } } System.out.println("上传图片完毕...") ;
发布成功图片后,记得拿到图片的original属性的值,这个值,在后面发布文章的body体中,是要再使用的。
发布文章代码
//设置Post请求头和header g3 = new HttpPost("https://mp.toutiao.com/core/article/edit_article_post/?source=mp&type=figure") ; g3.setHeader("Accept", "*/*") ; g3.setHeader("X-Requested-With", "XMLHttpRequest") ; g3.setHeader("Accept-Language", "zh-CN") ; g3.setHeader("Cache-Control", "no-cache") ; g3.setHeader("Connection", "Keep-Alive") ; g3.setHeader("Referer", "https://mp.toutiao.com/profile_v2/figure") ; g3.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko") ; g3.setHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8") ; String content01 = "abstract=&authors=&self_appoint=0&save=1&pgc_id=&urgent_push=0&is_draft=" + "&title=" + URLEncoder.encode(title, "utf-8") ;
拼接body体代码
com.alibaba.fastjson.JSONObject obj = new com.alibaba.fastjson.JSONObject() ; obj.put("url", "https://p3.pstatp.com/large/" + original[i]); obj.put("uri", original[i]); obj.put("ic_uri", ""); obj.put("product", new com.alibaba.fastjson.JSONObject()); obj.put("desc", photopath[i][1]); obj.put("web_uri", original[i]); obj.put("url_pattern", "{{image_domain}}"); obj.put("gallery_id", gallery_id); photoArr.add(obj) ; gallery_ids[i] = gallery_id ; //这部分代码有些乱,拼接的内容比较复杂了 content02 += "&gallery_data%5B"+gallery_id+"%5D%5Burl%5D=" + URLEncoder.encode("https://p3.pstatp.com/large/" + original[i], "utf-8") ; content02 += "&gallery_data%5B"+gallery_id+"%5D%5Buri%5D=" + original[i] ; content02 += "&gallery_data%5B"+gallery_id+"%5D%5Bic_uri%5D=" ; content02 += "&gallery_data%5B"+gallery_id+"%5D%5Bdesc%5D=" + URLEncoder.encode(photopath[i][1], "utf-8"); content02 += "&gallery_data%5B"+gallery_id+"%5D%5Bweb_uri%5D=" + original[i] ; content02 += "&gallery_data%5B"+gallery_id+"%5D%5Burl_ pattern%5D=%7B%7Bimage_domain%7D%7D&gallery_data%5B"+gallery_id+"%5D%5Bgallery_id%5D="+ gallery_id ; //拼接封面的JSON com.alibaba.fastjson.JSONArray coversArr = new com.alibaba.fastjson.JSONArray() ; if(fengmian.length > 0){ for(int index = 0 ;index<fengmian.length ; index ++){ int getind = fengmian[index] ; com.alibaba.fastjson.JSONObject obj = new com.alibaba.fastjson.JSONObject() ; long ids = Long.parseLong("1194457543001") ; if(index == 0){ ids = Long.parseLong("1194457543001") ; }else if(index == 0){ ids = Long.parseLong("1194457543002") ; }else if(index == 0){ ids = Long.parseLong("1194457543003") ; } obj.put("id", ids); obj.put("url", "https://p3.pstatp.com/list/" + original[getind]); obj.put("uri", original[getind]); obj.put("origin_uri", original[getind]); obj.put("ic_uri", ""); obj.put("thumb_width", 1022); obj.put("thumb_height", 756); coversArr.add(obj) ; } String pgc_feed_covers = coversArr.toJSONString() ; content05 = content05 +"&pgc_feed_covers="+ URLEncoder.encode(pgc_feed_covers, "utf-8") ; } //请求后台代码 StringEntity reqEntity = new StringEntity(content); g3.setEntity(reqEntity); response2 = httpClient.execute(g3); //httpClient.executeMethod(g1) ; sg1 = EntityUtils.toString(response2.getEntity(),"utf-8") ; sg1 = StringRandomUtils.unicodeToString(sg1) ; System.out.println(sg1);
模拟发送部分,开始是用JSON体自己转换的,但是试了几次,总是会失败,没找到原因,没办法,就把字符串全部模拟拼接来实现,没想到,竟然成功了,不过还好了,整体功能,都很正常。
小结
这样一个模拟代码,其实是经过很多次的抓包,试验才得到的结果,中间的艰辛,是很难想象的。就应了那么一句话,书,非借不能读也,越是容易得到的东西,越不会被珍惜,想共同探讨的朋友,记得留言, 或者联系我啊。