先上结果:

  之前 在公司业务中用过java+Selenium+ChromeDriver ,使用起来非常顺手,可以完美模拟真实的用户浏览行为。最近休息的时候想用C#也试一下,于是有了本文。

  实现原理一样,只是由java换成了C#。(ps:个人感觉就业务开发代码来说,熟悉之后两种语言可以无缝切换。)

  事先声明,代码中会采集用户登录咕咚网站之后的个人数据接口,如果此行为损害了咕咚网站的利益,请联系我删除或修改本文(我对采集行为一直本着每一次调用之后sleep的原则,毕竟不是为了把人家的网站搞死)。文中使用的数据是我自己的跑步数据,不涉及到其他用户的数据。

  项目结构:为了方便跑友使用,用的winform程序,附nuget包

以下是form1.cs的代码,删除了部分提示的 MessageBox.Show(),从产品角度来讲,应该添加loading提示和进度条,这里就只放代码了。需要安装最新的chrome浏览器+代码中使用的chromedriver是 v2.9.248315

 using Newtonsoft.Json;
using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms; namespace GuDongLine
{
public partial class Form1 : Form
{
public Form1()
{ InitializeComponent();
label2.Text = "请根据提示进行操作,本产品需要最新版本Chrome浏览器支持,网络良好的情况下运行,内部交流,不作为商业软件,如有侵权请扫中间微信二维码联系我。祝各位跑友生活愉快,身体健康,happy PB";
}
delegate void ChangeInvoke(int num);
public int Fun1;
private void button1_Click(object sender, EventArgs e)
{
try
{
Thread thread = new Thread(go);
thread.Start(); }
catch (Exception)
{
MessageBox.Show("操作异常,请重新打开,或联系我");
} }
private void ChangeNum()
{
MessageBox.Show("正在运行请等待!运行完毕后会有提示!");
}
public void go()
{
if (MessageBox.Show("下面请登录咕咚,本软件只会向咕咚官网发送和接受数据,如果发现软件向其他网址发送数据,说明本软件被修改,可能有病毒", "登录咕咚账号", MessageBoxButtons.OKCancel).Equals(DialogResult.OK))
{
ChromeOptions options = new ChromeOptions();
options.AddArguments("--test-type", "--ignore-certificate-errors");
IWebDriver driver = new ChromeDriver(System.Environment.CurrentDirectory, options);
driver.Url = "http://www.codoon.com/home";
Thread.Sleep(); driver.Navigate().GoToUrl("http://www.codoon.com/gps_sports/my_routes"); Thread.Sleep();
int runCount = int.Parse(driver.FindElement(By.Id("current_index")).Text) / ;
for (int i = ; i < runCount + ; i++)
{
try
{
driver.FindElement(By.ClassName("more_data")).Click();
Thread.Sleep();
}
catch (Exception)
{
if (i > runCount + )
{
break;
} }
}
Thread thread2 = new Thread(ChangeNum);
thread2.Start();
string userId = driver.FindElement(By.ClassName("home_user_header")).GetAttribute("src").Substring(, );
string data = "";
dynamic va = driver.FindElements(By.ClassName("detail_sports_content"));
foreach (IWebElement iwe in va)
{
using (var client = new WebClient())
{
Thread.Sleep();
var responseString = client.DownloadString("http://www.codoon.com/gps_sports/route?user_id=" + userId + "&route_id=" + iwe.FindElement(By.TagName("table")).GetAttribute("id") + "&need_next=1&_=1520349266435"); var DynamicObject = JsonConvert.DeserializeObject<dynamic>(responseString);
string ss = "";
foreach (var s in DynamicObject.line)
{
ss += ((double)s[] + 0.0062).ToString() + "," + ((double)s[] + 0.00135).ToString() + ";";
}
ss = ss.Remove(ss.Length - );
data += ss + "&";
}
}
data = data.Remove(data.Length - );
driver.Close();
string html = @"<!DOCTYPE html><html lang='zh - CN\'>
<head> <meta charset = 'utf-8'> <meta http - equiv = 'X-UA-Compatible' content = 'IE=edge'> <meta name = 'viewport' content = 'width=device-width, initial-scale=1'> <title> my_all_run_lines </title> <link rel = 'stylesheet' href = 'http://cache.amap.com/lbs/static/main1119.css' />
</head>
<body>
<div id = 'container' class='container'></div>
<script src = 'http://webapi.amap.com/loca?key=6ac3f558819c2c4711ea0c0a37192137'></script>
<script src='http://a.amap.com/Loca/static/dist/jquery.min.js'></script>
<script> var map = Loca.create('container', {
key: '1e387d3db027b46a23600cf7f2ed7344',
mapStyle: 'amap://styles/grey',
features: ['bg', 'road'],
zoom: 10
}); var layer = Loca.visualLayer({
container: map,
type: 'line',
shape: 'line',
}); $.get('http://a.amap.com/Loca/static/mock/buslines.txt', function (data) {
data = '" + data + "';" + "" +
"" +
"" +
"" +
"" +
"" + @"var lines = data.split('&').map(function (item) {
return {
linePath: item.split(';').map(function(lnglat) {
return lnglat.split(',');
})
};
}); layer.setData(lines, {
lnglat: 'linePath'
});
layer.setOptions({
style: {
opacity: 0.2,
lineWidth: 2,
stroke: '#b7eff7',
}
}); layer.render();
}) </script>
</body>
</html>"; string path = System.Environment.CurrentDirectory + "\\map.html";
File.WriteAllText(path, html);
}
else
{
System.Environment.Exit();
}
} }
}

  说一下思路:

    1.确定使用的语言和采集框架。

    2.确定数据来源,从咕咚app还是咕咚网站,如果咕咚网站没有提供接口,估计就得去研究app了。本文中耗费时间的一个点就在研究咕咚网站的请求数据的规则上了,原则来说,只要正常用户能打开的网页,无法挡住采集工具。

    3.数据的展示:找了百度地图和高德地图的api接口,两个也都分别试了一下,其实咕咚网本身是使用的谷歌的,但是谷歌的接口研究起来不太方便,最后从数据展示的效果上选择了高德地图,顺便说一下,从咕咚采集的跑步数据其实是一组一组的经纬度坐标,而且是被阉割的经纬度,精确度不够,所以地图上显示的时候会有偏差,随着跑步距离的增长,数据量也比较大,以文中1700公里的跑量,生成的展示html是2.42M ,我家里的古董笔记本加载的时间比较长。

  另:很久以前跟咕咚的客服打过电话,想要一个集成自己所有跑步路线的功能,貌似答应的很好,但是没发现app中有此功能(本文的由来?),当然,如果提供这个功能,一下加载这么多数据,对服务器来说不太好,但是可以迂回一下嘛,比如只生成一个月之前的?申请之后生成?个人感觉有许多解决办法,而且也能做成一种营销活动。

  欢迎探讨,欢迎指教,谢谢大家,希望每一个程序员身体健康。

                                                                    by:跑过16+17北马的coder

    

用C#+Selenium+ChromeDriver 生成我的咕咚跑步路线地图的更多相关文章

  1. ASP.NET MVC WebApi 返回数据类型序列化控制(json,xml) 用javascript在客户端删除某一个cookie键值对 input点击链接另一个页面,各种操作。 C# 往线程里传参数的方法总结 TCP/IP 协议 用C#+Selenium+ChromeDriver 生成我的咕咚跑步路线地图 (转)值得学习百度开源70+项目

    ASP.NET MVC WebApi 返回数据类型序列化控制(json,xml)   我们都知道在使用WebApi的时候Controller会自动将Action的返回值自动进行各种序列化处理(序列化为 ...

  2. selenium + ChromeDriver 实战系列之启信宝(一)

    之前写了一篇selenium + ChromeDriver的一些入门的知识,这篇博客里面找了启信宝这个网站,简单的进行了一个实战练习.本篇博客的结构如下:       首先会给出一些使用seleniu ...

  3. 一次失败的Selenium chromedriver切换

    背景 Selenium webdriver一直使用Firefox作为浏览器来跑webtest, 但是最近发现ff有时会报超时的错误,于是想到使用chromedriver来提升稳定性.本想只把.fire ...

  4. 使用Selenium+ChromeDriver登录微博并且获取cookie

    using OpenQA.Selenium;using OpenQA.Selenium.Chrome; public class GetSinaCookie { private static stri ...

  5. 无界面Ubuntu服务器搭建selenium+chromedriver+VNC运行环境

    搭建背景 有时候我们需要把基于selenium的爬虫放到服务器上跑的时候,就需要这样一套运行环境,其中VNC是虚拟的显示模式,用于排查定位线上问题以及实时运行情况. 搭建流程 安装虚拟输出设备:sud ...

  6. centos7无GUI运行selenium chromedriver 亲测可用!

    1. 安装chrome 首先安装google的epel源 vi /etc/yum.repos.d/google.repo [google] name=Google-x86_64 baseurl=htt ...

  7. scrapy+selenium+chromedriver解析动态渲染页面

    背景:动态页面是页面是通过js代码渲染出来的,无法直接使用scrapy爬虫,这是就需要先把js代码转为静态的html,再用scrapy爬虫就可以解决 解决办法:增加SeleniumMiddleware ...

  8. selenium:chromedriver与chrome版本的对应关系

    转自:http://blog.csdn.NET/huilan_same/article/details/51896672 再使用selenium打开chrome浏览器的时候,需要用chromedriv ...

  9. 19.Selenium+Python生成测试报告

    1.代码如下所示: from selenium import webdriver import unittest import HTMLTestRunner class BaiduSearch(uni ...

随机推荐

  1. 高性能伪事务之Lua in Redis

    EVAL简介 Redis2.6加入了对Lua脚本的支持.Lua脚本可以被用来扩展Redis的功能,并提供更好的性能. 在<Redis拾遗>中曾经引用了<Redis in Action ...

  2. MySQL----下载安装

    MySQL 的官网下载地址:http://www.mysql.com/downloads/ 注意 1. MySQL Community Server 社区版本,开源免费,但不提供官方技术支持.2. M ...

  3. JsRender实用入门教程

    这篇文章主要介绍了JsRender实用入门实例,包含了tag else使用.循环嵌套访问父级数据等知识点,并提供了完整的实例下载,非常具有实用价值,需要的朋友可以参考下     本文是一篇JsRend ...

  4. Photoshop 操作

    本文主要记录在工作过程中使用ps的一些快捷键或操作顺序 1.ctrl+H:取消标尺 2.ctrl+D:取消选区 3.看矩形尺寸:选中矩形图层 >窗口 >属性(w:宽  H:高) 4.看图层 ...

  5. JDBC url连接字符串错误1

    String url="jdbc:mysql://127.0.0.1:3306/northwind?useUnicode=true&characterEncoding=utf-8&a ...

  6. Numpy的基本概念

    来源:https://www.numpy.org/devdocs/user/quickstart.html 轴:即维度 eg. [1, 2, 1],有一个轴 [[ 1, 0, 0],[ 0, 1, 2 ...

  7. [转] JavaScript中in操作符(for..in)、Object.keys()和Object.getOwnPropertyNames()的区别

    ECMAScript将对象的属性分为两种:数据属性和访问器属性.每一种属性内部都有一些特性,这里我们只关注对象属性的[[Enumerable]]特征,它表示是否通过 for-in 循环返回属性,也可以 ...

  8. EF Core 2.2 对多个 DbContext 多个数据库的情况进行迁移的示例

    目录 场景 创建新项目 创建第一个模型 创建第二个模型 使用依赖注入注册上下文 创建数据库 场景 在一个项目中,使用了多个 DbContext 且每个 DbContext 对应一个数据库的情况 创建新 ...

  9. 手把手教你从ESXI部署到vSphere web Client管理控制

    作为实验环境,一台物理机即可 既然是实验环境,那么首先把这个物理机装成ESXI6.5的宿主机并配置网络系统 第二步骤就是在ESXI上面导入OVF文件,注册一台虚机,作为数据管理中心 第三步骤就是基于这 ...

  10. [数据结构] 树状数组 的C程序实现

    ];//树状数组,用于取区间[x,y]的数据的和 /* & 特殊运算,t&(-t)的值(十进制),就是t在2进制下,从右往左数第一个1出现的位置. 结合树状数组的特殊性质,这个值有用 ...