.net core之ACG小站爬虫(一)

想到好久没写过.net的代码了,因此就尝试来写一写.net的代码。此外,也想要熟悉一下Phantomjs。


环境配置

  • .net core下载。可选的可以下载宇宙大IDEVisual Studio,当然更加推荐使用Visual Studio Code进行代码的书写。

  • Phantomjs。这个不用说了,今天的主角。采用无头浏览器爬取ACG小站的很大原因是它的页面很难分析,此外也有熟悉一下Phantomjs的意思。

  • HttpCode.Core,是一个.net core的HTTP请求库,可以使用Nuget安装。库本质上是基于HttpWebRequest实现的,但是为了舍去我们自己封装的麻烦,就采用此库来减少代码量。

  • AngleSharp,一个帮助解析HTML的.net库,可以直接使用LINQ来查询,很方便。

页面分析


详情页

就以其中随便的一个动漫链接下载为例子,发现了坑爹的玩意,下载链接的href属性是javascript:;。介于浅薄的javascript知识,百度了才知道这个是伪协议,实际运行了一段js代码回调,从而打开了新的页面。但是这个网站是用React写的,更可怕的在下面。


JavaScript

原本采用直接分析,并借助Debug调试来分析它点击后的回调函数,但是这个JavaScript文件竟然有2万行代码,Chrome打开调试定点,根本就没法format,不然直接卡死,于是放弃了这个念头,转而采用无头浏览器进行爬取。

实现

一般采用Phantomjs的手段都为Selenium+Phantomjs,但是性能不是很好,而且暂时也没怎么找.netSelenium接口,所以就选用了最近看到的直接让Phantomjs作为服务端,然后去请求它,让它把要爬取的结果反馈给.net。注意,这里的结果可以是网页页面,也可以是Phantomjs进行HTML解析完的真实数据。

.Net Core代码

        public async Task<string> GetDownloadPageAsync(string url){string result = string.Empty;//请求phantomjs 获取下载页面string dom = "Tappable-inactive animated fadeIn";KeyValuePair<string, string> url2dom = new KeyValuePair<string, string>(url, dom);var postData = JsonConvert.SerializeObject(url2dom);CookieContainer cc = new CookieContainer();  HttpHelpers helper = new HttpHelpers();  HttpItems items = new HttpItems();HttpResults hr = new HttpResults();items.Url = "http://localhost:8088/";items.Method = "POST";items.Container = cc;items.Postdata = postData;items.Timeout = 100000;hr = await helper.GetHtmlAsync(items);var downloadPageUrl = hr.Html;Console.WriteLine($"first => { downloadPageUrl }");if(downloadPageUrl.Contains("http")){# TODO}else{result = downloadPageUrl; //输出错误信息}return result;}

这里展示的是第一部分的代码,即请求详情页的代码,还未涉及到点了下载按钮之后页面的分析,实际上差不多。

JavaScript代码

"use strict";
var port = 8088;
var server = require('webserver').create();//服务端监听
server.listen(8088, function (request, response) {//传入的参数有待更改,目前为//{"Key":"https://acg12.com/200340/", "Value":"Tappable-inactive animated fadeIn"}的json字符窜//第一个参数为详情页,第二个为下载按钮的Domvar data = JSON.parse(request.postRaw);var url = data.Key.toString();var dom = data.Value.toString();var code = 0;var page = require('webpage').create();//初始化headerspage.onInitialized = function() {page.customHeaders = {};};page.settings.loadImages = false;page.customHeaders = {"User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.120 Safari/537.36","Referer": url};response.headers = {'Cache': 'no-cache','Content-Type': 'text/plain','Connection': 'Keep-Alive','Keep-Alive': 'timeout=20, max=100'};//根据Phantomjs的官网,这个回调在打开新标签页会触发page.onPageCreated = function(newPage) {//console.log('A new child page was created! Its requested URL is not yet available, though.');newPage.onLoadFinished = function(status) {console.log('A child page is Loaded: ' + newPage.url);//newPage.render('newPage.png');response.write(newPage.url);response.statusCode = code;response.close(); //写入返回给.net端的响应内容。};};//让Phantomjs帮助我们去请求页面page.open(url, function (status) {console.log("----" + status);if (status !== 'success') {code = 400;response.write('4XX');response.statusCode = code;response.close();} else {code = 200;window.setTimeout(function (){//执行JavaScript代码,类似于在浏览器Console中执行JavaScriptpage.evaluate(function(dom) {console.log(dom);var btnList = document.getElementsByClassName(dom);if(btnList.length > 0){var btn = document.getElementsByClassName(dom)[1]; // 获取下载按钮btn.click(); //点击下载按钮,打开新标签页,触发page.onPageCreated回调函数。}}, dom);        }, 7000);}});//根据Phantomjs的官网,这个回调主要应对执行evaluate函数内部的console.log输出,因为两个环境是隔离的。page.onConsoleMessage = function(msg, lineNum, sourceId) {console.log("$$$$$" + msg);};page.onError = function(msg, trace) {var msgStack = ['PHANTOM ERROR: ' + msg];if (trace && trace.length) {msgStack.push('TRACE:');trace.forEach(function(t) {msgStack.push(' -> ' + (t.file || t.sourceURL) + ': ' + t.line + (t.function ? ' (in function ' + t.function +')' : ''));});}console.log(msgStack.join('\n'));phantom.exit(1);};
});
phantom.onError = function(msg, trace) {var msgStack = ['PHANTOM ERROR: ' + msg];if (trace && trace.length) {msgStack.push('TRACE:');trace.forEach(function(t) {msgStack.push(' -> ' + (t.file || t.sourceURL) + ': ' + t.line + (t.function ? ' (in function ' + t.function +')' : ''));});}console.log(msgStack.join('\n'));phantom.exit(1);};

这里的注释比较详细,就不细说了。

启动上述Phantomjs服务端的脚本

phantomjs  --ssl-protocol=any --debug=true .\server_get_detail_page.js 

第一个参数是为了保持ssl的链接,第二个参数开启debug,第三个参数为上面的JavaScript代码。

还有之后的页面类似于以上的代码,但是有一些细节需要注意,为了防止文章过长(明明是再水一篇(๑ ̄ _   ̄๑))大家下期再见。等等,完整的源代码就先全放在Github上了


原文地址:http://www.jianshu.com/p/9b738a25b585


.NET社区新闻,深度好文,微信中搜索dotNET跨平台或扫描二维码关注

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/323559.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

数组:完成等差等比数列,及其他数列

有没有想过用c语言来编写一些数列呢&#xff0c;编写数列&#xff0c;数组是最好的选择。 等差&#xff1a; #include<stdio.h> main(){ int a[1000],b,i;a[0]1;scanf("%d",&b);for(i0;i<b;i){a[i1]a[i]2;}for(i0;i<b;i){printf("a%d%-8d\t&q…

Spring MVC竟然有5种参数绑定的方式?你知道几种?

转载自 Spring MVC竟然有5种参数绑定的方式&#xff1f;你知道几种&#xff1f; SpringMVC参数绑定&#xff0c;简单来说就是将客户端请求的key/value数据绑定到controller方法的形参上&#xff0c;然后就可以在controller中使用该参数了下面通过5个常用的注解演示下如何进行参…

微服务中如何切换配置文件、部署

大家好&#xff0c;我是雄雄&#xff0c;欢迎关注微信公众号&#xff1a;雄雄的小课堂 最近改了个微服务的项目&#xff0c;在本身的业务上加上一个演示的业务。 以前没有弄过微服务的项目&#xff0c;这次改完之后&#xff0c;部署花了不少时间&#xff0c;一方面是因为服务器…

P1063-能量项链【区间dp】

正题 评测记录:https://www.luogu.org/recordnew/lists?uid52918&pidP1063 题目大意 有n个珠子组成环&#xff0c;每颗珠子的头尾标记连接&#xff0c;将两颗珠子合并会产生headi∗headj∗tailjheadi∗headj∗tailj的能量&#xff0c;产生一颗头标记为头珠子的头标记&am…

asp.net core mvc View Component 应用

ViewComponent1、View 组件介绍在ASP.NET CORE MVC中,View组件有点类似于partial views,但是他们更强大&#xff0c;View组件不能使用model binding,当你调用它的时候仅仅依赖与你提供的数据一个View组件特点&#xff1a;.呈现一大块而不是一个整体的响应。 .包含在控制器和视…

分离三位数

#include<stdio.h> main(){ int k,l,m,n;printf("请输入一个三位数"); scanf("%d",&k);lk/100;mk/10%10;nk%10;printf("这个三位数的百位是:%d\n",l);printf("这个三位数的十位是:%d\n",m);printf("这个三位数的个位是…

@Autowired注入RedisCache报错空指针

大家好&#xff0c;我是雄雄&#xff0c;欢迎关注微信公众号&#xff1a;雄雄的小课堂 今天在改一个几年前写的项目中的代码&#xff0c;其中有个地方用到了缓存&#xff0c;不过代码中用的是&#xff1a;CacheUtils,也不是报错&#xff0c;但是就是在并发的时候有问题&#xf…

P1351-联合权值【树形结构】

正题 评测记录:https://www.luogu.org/recordnew/lists?uid52918&pidP1351 题目大意 一棵树&#xff0c;每个点有权值&#xff0c;求两个距离为2的点使权值之积最大和所以这种点对的权值之积的和。 解题思路 分为两种情况&#xff1a; 1.一个点是另一个点的爷节点&…

MySQL - InnoDB特性 - Buffer Pool漫谈

转载自 MySQL - InnoDB特性 - Buffer Pool漫谈 缓存管理是DBMS的核心系统&#xff0c;用于管理数据页的访问、刷脏和驱逐&#xff1b;虽然操作系统本身有page cache&#xff0c;但那不是专门为数据库设计的&#xff0c;所以大多数数据库系统都是自己来管理缓存。由于几乎所有…

小程序中安装@vant依赖

大家好&#xff0c;我是雄雄。 今天给大家分享一篇&#xff0c;关于小程序中如何安装vant依赖 小程序中安装vant依赖 cd .\miniprogram\ npm i vant/weapp -S --production 将node_modules文件夹下面的vant复制到miniprogram_npm文件夹下面 构建npm 如果报错&#xff1a;”…

大湾区第二次.NET技术交流会圆满成功

2017年9月16日的深圳阳光明媚&#xff0c;一场为庆祝.NET Core 2.0发布和.NET 社区大会&#xff08;https://www.dotnetconf.net/&#xff09; 的召开的本地社区活动&#xff0c;这次活动还得到如鹏网杨中科老师的大力支持开通网上直播&#xff0c;网上有300多位参与活动&#…

五分钟轻松了解Hbase面向列的存储

转载自 五分钟轻松了解Hbase面向列的存储 说明&#xff1a;从严格的列式存储的定义来看&#xff0c;Hbase并不属于列式存储&#xff0c;有人称它为面向列的存储&#xff0c;请各位看官注意这一点。 行式存储 传统的数据库是关系型的&#xff0c;且是按行来存储的。如下图&a…

P1282-多米诺骨牌【dp,背包】

正题 评测记录:https://www.luogu.org/recordnew/lists?uid52918&pidP1282 题目大意 n个多米诺骨牌&#xff0c;上下值不相同&#xff0c;可以交换一个多米诺上下的值&#xff0c;求最少的交换次数使上下之和的差值最小。 解题思路 用fi,jNfi,jN表示只计算前i个多米诺&…

判断是否为素数

#include<stdio.h> main(){ int m,n;printf("请输入一个数我来给你判断是否是素数&#xff1a;\n");scanf("%d",&m);if((m%40&&m%100!0)||m%4000){printf("该数是素数");} else{printf("该数不是素数");} }

jeecg微服务项目调用接口报错Token验证失效的解决方法

大家好&#xff0c;我是雄雄。 前言 今天&#xff0c;记录一篇啼笑皆非的问题。 昨晚上在做微信公众号开发时&#xff0c;遇到了个解决好久的问题&#xff0c;即&#xff1a;微信公众号上配置服务器信息之后&#xff0c;回调服务器接口总是报错token验证失败。 剧透&#xf…

是时候开始用C#快速开发移动应用了

从2015年接触Xamarin到至今已经2个年头&#xff0c;我对Xamarin的技能没有长进多少&#xff0c;但它却已经足够成熟到在跨平台移动开发工具中占有一席之地。在扫了一些资料之后&#xff0c;突然发现国外有很多移动端的应用已经是用Xamarin开发&#xff0c;Telerik还有专门的团队…

P1525-关押罪犯【并查集】

正题 评测记录:https://www.luogu.org/recordnew/lists?uid52918&pidP1525 题目大意 有n个罪犯&#xff0c;罪犯有些关系&#xff0c;就是(i,j,c)(i,j,c)表示罪犯i和罪犯j在同一个监狱会造成c的破坏&#xff0c;有两座监狱&#xff0c;要求分配的使得最大的破坏最小。 解…

十分钟理解负载均衡

转载自 十分钟理解负载均衡 开头先理解一下所谓的“均衡” 不能狭义地理解为分配给所有实际服务器一样多的工作量&#xff0c;因为多台服务器的承载能力各不相同&#xff0c;这可能体现在硬件配置、网络带宽的差异&#xff0c;也可能因为某台服务器身兼多职&#xff0c;我们…

输入一个字母,转大小写

用scanf完成 #include<stdio.h> main(){char m;scanf("%c",&m);if(m>a&&m<z){mm-32;}else if(m>A&&m<Z){m32;}printf("%c",m);}用getcahr完成 #include<stdio.h> main(){char m;mgetchar();if(m>a&&…

nginx中配置不输入端口(指定地址)访问项目的方法

大家好&#xff0c;我是雄雄&#xff0c;欢迎关注微信公众号&#xff1a;雄雄的小课堂。 需求 访问项目时&#xff0c;地址是这样的&#xff1a;http://152.189.xxx.xxx:7890,绑定了域名之后&#xff0c;需要这样访问&#xff1a;http://XXXX.com:7890,想要换成http://152.189…