.net core之ACG小站爬虫

发表于:2017-9-19 10:16

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:EmptyChan    来源:51Testing软件测试网采编

#
DotNet
分享:
  环境配置
  ●.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,但是性能不是很好,而且暂时也没怎么找.net的Selenium接口,所以就选用了最近看到的直接让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字符窜
      //第一个参数为详情页,第二个为下载按钮的Dom
      var data = JSON.parse(request.postRaw);
      var url = data.Key.toString();
      var dom = data.Value.toString();
      var code = 0;
      var page = require('webpage').create();
      //初始化headers
      page.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中执行JavaScript
                  page.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上了。
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号