【请抓紧时间上车】实现一个12306的chrom插件

源码地址: github.com/zjians/1230…

起因(吐槽):

刚过完年不久,相信大家还能回想到春运时被抢票支配的恐惧。但是本教程并不是教大家如何刷票的。半个月前我帮一个朋友买一张从宿州到上海的火车票,结果。。效果类似下图WTF:


于是想着买个中间站的票能上车也行,到车上再补票就好了,于是对着车次点了一下,将中间站的名称ctrl+c,ctrl+v一个一个查询有没有余票。一顿操作猛如虎,但是凭着程序员的自尊,emm这样不行,操作太傻了。于是周末便撸了这款插件:



嗯嗯,优雅,程序员的自尊又回来了


经过(开发过程):

首先打开chrome开发文档 crxdoc-zh.appspot.com/extensions/…

本来以为要刷很多文档,结果看了20分钟,嗯,感谢自己是个前端,会了。

首先基础工作就是定义一个manifest.json (清单文件),用于定义插件相关的配置。先贴一份本插件的配置文件(各项作用看注释),详细解释请看官方文档

{     "manifest_version": 2,    "name": "12306",    "version": "1.0",    "description": "查询当前车次各站点的余票",    "author": "https://github.com/zjians/12306",    "page_action": {        "default_icon": {            "16": "assets/icons/icon16.png",            "48": "assets/icons/icon48.png",           "128": "assets/icons/icon128.png"        },        "default_title": "12306上车票"    },     "content_scripts": [{         "matches": ["https://*.12306.cn/*"],         "js": ["js/jquery.min.js","js/main.js"],         "css": ["assets/styles/main.css"],         "run_at": "document_start"    }],    "web_accessible_resources": ["assets/images/*" ]  }复制代码

如果你不想看文档,那么我整理了一份比较全的manifest解释,几乎覆盖了常用的所有设置,可用于快速查询:

{
 "manifest_version": 2,
 /*
   指定您的应用包要求的清单文件格式的版本。从 Chrome 18 开始,开发人员应该指定 2
 */

 "name":"我的应用名称",
 "version":"我的应用版本",

 "default_locale":"zh", // 默认语言
 /*
   对于含有 _locales 目录的应用来说这一属性是必需的,指定_locales中的子目录,包含该应用默认字符串。
   在没有 _locales 目录的应用中该属性不能存在
 */

 "description":"关于应用的描述",

 "icons":{ /*可定义一个或多个, 应用或主题背景的图标*/
   "16":"icon16.png",
   "48":"icon48.png",
   "128": "icon128.png"
 },

 /*
  下面的browser_action或page_action选择某一个使用
  如果插件只对特定页面有效,则使用page_action(页面按钮),(比如抢票插件,只对12306网站有效)
  如果插件对所有页面都有效,则使用browser_action浏览器按钮),(比如截图插件,对所有页面都有效)
 */
 "browser_action": { // 如果有 browser_action, 即在chrome toolbar 的右边添加了一个 icon,
   "default_icon": "test.jpg",
   "default_title": "Google Mail",      // tooltip, 光标停留在 icon 上时显示
   "default_popup": "popup.html"  // 如果有 popup 的页面, 则用户点击图标就会渲染此 HTML 页面
 },

 "page_action":{ // 如果 page_action 并不应用在当前页面, icon会显示灰色
   "default_icon": {
     "19": "images/icon19.png",
     "38": "images/icon38.png"
   },
   "default_title": "Google Mail",
   "default_popup": "popup.html"
 },

 //可选
 "author":"开发者",
 "automation":"",


 "background":{
   "scripts":["background.js"],
   "page": "background.html",
   "persistent":false
 },
 /*
  后台网页
  1.应用通常需要有一个长时间运行的脚本来管理一些任务或状态,而后台网页就是为这一目的而设立。
  通常情况下,后台页面不需要任何 HTML 标记,这种情况下后台页面可以单独使用 JavaScript文件实现。
  后台页面将由应用系统生成,包含 scripts 属性中列出的每一个文件。
  2.page:如果您需要在您的后台页面中指定 HTML,您可以改用 page 属性:
  3.persistent:应用和应用通常需要长时间运行的脚本来管理某些任务或状态,这就是事件页面的作用。
  事件页面只在需要时加载,当事件页面不活动时就会卸载,以便释放内存和其他系统资源。
  如何得到事件页面 就是设置一个"persistent"键,如果没有设置,你将得到一个普通的后台页面。
 */


 "content_scripts": [{
   "matches": ["https://*.domain.com/*"], // 匹配的地址网页
   "exclude_matches":[],
   "js": ["jquery.js","yourScript.js"], // 内容脚本
   "css": ["yourStyles.css"], // 在页面上添加的css样式
   "run_at":"document_idle",
   "all_frames": true //该匹配下面的所有窗口
 },{ // 可以针对不同的规则插入不同的内容
   "matches": ["*://*/*.png", "*://*/*.jpg", "*://*/*.gif", "*://*/*.bmp"],
   "js": ["js/show-image-content-size.js"]
 }],
 /*
   内容脚本: 其实就是向你想要的网页中插入一个脚本代码,执行你想要做的事情
            内容脚本是在网页的上下文中运行的 JavaScript 文件,
            它们可以通过标准的文档对象模型(DOM)来获取浏览器访问的网页详情,或者作出更改。
   
   1.run_at 可选。
   控制 js 中的 JavaScript 文件何时插入,
   可以为 "document_start""document_end""document_idle",默认为 "document_idle"。 

      1.1如果是 "document_start",这些文件将在 css 中指定的文件之后,但是在所有其他 DOM 构造或脚本运行之前插入。 

      1.2.如果是 "document_end",文件将在 DOM 完成之后立即插入,但是在加载子资源(如图像与框架)之前插入。 

      1.3.如果是 "document_idle",浏览器将在 "document_end" 和刚发生 window.onload 事件这两个时刻之间选择合适的时候插入,
      具体的插入时间取决于文档的复杂程度以及加载文档所花的时间,并且浏览器会尽可能地为加快页面加载速度而优化。 
    
   2.all_frames 可选。
   控制内容脚本运行在匹配页面的所有框架中还是仅在顶层框架中。 默认为 false,意味着仅在顶层框架中运行
 */

"web_accessible_resources": [ // 普通页面能够直接访问的插件资源列表,如果不设置是无法直接访问的
   "images/*.png",
   "style/double-rainbow.css",
   "script/double-rainbow.js",
   "script/main.js",
   "templates/*"
 ],

 "update_url": "你的插件在chrome商店的地址", // 如果你使用 Chrome 开发者信息中心发布的扩展程序,可忽略这一项
 // 如果你想从自己的服务器上更新插件,则需要指定update_url,指向你的服务器地址。

"homepage_url": "https://www.xxx.com", // 插件的主页

"permissions":[
   "tabs", // 如果扩展使用chrome.tabs或chrome.windows模块,则添加此项
   "bookmarks", // 使用chrome.bookmarks模块来创建、组织和管理书签
   "http://www.blogger.com/",    
   "http://*.google.com/",    
   "unlimitedStorage", // 提供了一个无限的HTML5配额来存储客户端数据,如数据库和本地存储文件。没有这个权限,扩展仅限于5 MB的本地存储
   "history" // 历史记录的使用权限  chrome.history 
   "notifications",// 提示
   "cookies",// 如果扩展程序使用chrome.cookies模块,则添加此项
 ],
 /*  
   扩展或app将使用的一组权限。每个权限是一列已知字符串列表中的一个,
   如geolocatioin或者一个匹配模式,来指定可以访问的一个或者多个主机。
   权限可以帮助限定危险,如果你的扩展或者app被攻击。
   有些权限在安装之前,会告知用户
*/

key:'',
/**开发时为扩展指定的唯一标识值。
  注意:通常您并不需要直接使用这个值,而是在您的代码中使用相对路径或者chrome.extension.getURL()得到的绝对路径。
  这个值并不是开发时显式指定的,而是Chrome在安装.crx时辅助生成的。(开发时可以通过上传扩展或者手工打包生成crx文件)。 
  安装完crx,在Chrome的用户数据目录下的Default/Extensions/<extensionId>/<versionString>/manifest.json文件中,您可以看到这个扩展的key。
**/

"commands": {
     // commands API 用来添加快捷键
     // 需要在 background page 上添加监听器绑定 handler
   "toggle-feature-foo": {
     "suggested_key": {
       "default": "Ctrl+Shift+Y",
       "mac": "Command+Shift+Y"
     },
     "description": "Toggle feature foo",
     "global": true
       // 当 chrome 没有 focus 时也可以生效的快捷键
       // 仅限 Ctrl+Shift+[0..9]
   },
   "_execute_browser_action": {
     "suggested_key": {
       "windows": "Ctrl+Shift+Y",
       "mac": "Command+Shift+Y",
       "chromeos": "Ctrl+Shift+U",
       "linux": "Ctrl+Shift+J"
     }
   },
   "_execute_page_action": {
     "suggested_key": {
       "default": "Ctrl+Shift+E",
       "windows": "Alt+Shift+P",
       "mac": "Alt+Shift+P"
     }
   },
   ...
 },
 "content_capabilities": ...,
 "optional_permissions": ["tabs"], // 其他需要的 permission, 在使用 chrome.permissions API 时用到, 并非安装插件时需要

 "short_name": "Short Name", // 插件名字简写

"storage": {//  使用 storage.managed api 的话, 需要一个 schema 文件指定存储字段类型等, 类似定义数据库表的 column
   "managed_schema": "schema.json"
 },
......
}复制代码

嗯,配置完以后,就可以在页面中插入自己的脚本了,于是就可以为所欲为了。

这里说下开发中遇到的三个问题:

1. 获取站点的缩写码,比如【北京北】的站点码为VAP,因为请求数据的时候传过去的参数,使用的不是站点中文名称,而是站点码。

于是我在网站中发现了这么一个变量:station_names,如下图所示:


     很显然解析这个变量就可以获得站点对应的站点码了,但是chrome插件和原始网页是两个相互分开的运行环境,也就是说我在插件的脚本中无法获取页面脚本中的变量。但是插件是可以获取页面的dom内容的,于是把station_names挂到dom上,然后在插件中获取dom上的属性。这样便通过dom获取到了页面脚本中的变量值,代码如下:

const script = document.createElement('script');script.type = 'text/javascript';script.innerHTML = "document.body.setAttribute('data-station-name', station_names);";document.head.appendChild(script);document.head.removeChild(script);const station_names = document.body.getAttribute('data-station-name');复制代码

2. 解析12306返回的数据

你可能会问,解析数据不是很简单的吗?我也是这么认为,但是直到我看到了他的返回:


自己解析肯定是不现实了,那么就找找网站的脚本是如何解析这个数据的吧,于是我找到了这个函数,就是他了:


经过上面函数的处理,得到了我想要的结果对象:

完美!

3. 本来以为可以开心的玩耍了,但是第二天一试,居然请求不到数据了。。

查看请求地址才发现,原来查询地址是每天都变的,好在请求失败以后,会返回可用的地址,于是在插件运行时,检测一下目前可用的请求地址:

let queryUrl = 'leftTicket/queryZ' // 请求地址$.ajax({    type: 'GET',    url: `https://kyfw.12306.cn/otn/${queryUrl}?leftTicketDTO.train_date=2019-02-20&leftTicketDTO.from_station=VNP&leftTicketDTO.to_station=NKH&purpose_codes=ADULT`,    error: function (res) { // 如果失败了,会返回可用的地址      queryUrl = res.responseJSON.c_url    }  })复制代码

ok!完成。

结束语:

1.如果觉得有用,请反手给个star鼓励一下

2.请上车后补票 ?

感谢观看

                                                


转载于:https://juejin.im/post/5c7bb1b56fb9a049b07e12a6

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值