Source code address: github.com/zjians/1230…

Cause:

Just after the New Year, I believe you can still recall the Spring Festival travel rush dominated by the fear of snatching tickets. But this tutorial is not about how to swipe tickets. Half a month ago I helped a friend buy a train ticket from Suzhou to Shanghai. The effect is similar to WTF below:



So thinking of buying a middle station ticket can also get on the bus, to the car to make up the ticket is good, so to the train point, the name of the middle station CTRL + C, CTRL + V a query there is no spare ticket. The operation is like a tiger, but with programmer’s pride, EMM does not work like this, the operation is stupid. So I masturbated this plugin over the weekend:





Yeah, elegance, programmer pride is back


Through (the development process) :

First open chrome crxdoc-zh.appspot.com/extensions/ document…

I thought I was going to have to brush a lot of documents, but I ended up looking at it for 20 minutes.

The first basic work is to define a manifest.json (manifest file) that defines the plugin-specific configuration. First paste a configuration file of this plug-in (see the notes for each function), please see the official documentation for detailed explanation:

{     "manifest_version": 2."name": "12306"."version": "1.0"."description": "Query the remaining tickets at each stop of the current train"."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": "Ticket on 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/*"]}Copy the code

If you don’t want to look at the documentation, I’ve compiled a fairly complete manifest explanation that covers almost all of the commonly used Settings for quick lookup:

{
 "manifest_version": 2, /* Specifies the version of the manifest file format required by your application package. Starting with Chrome 18, developers should specify 2 */"name":"Name of my app"."version":"My version of the app"."default_locale":"zh"// Default language /* This property is required for applications that contain the _locales directory, specifying subdirectories in _locales that contain the default strings for the application. This property cannot exist */ in applications without the _locales directory"description":"Description of the application."."icons":{/* Can define one or more, application or theme background ICONS */"16":"icon16.png"."48":"icon48.png"."128": "icon128.png"}, /* Browser_action or page_action below select one to use. If the plugin only works on certain pages, use page_action (page button). (for example, the ticket plugin only works on 12306.com.) Use the Browser_action browser button), (such as the screenshot plugin, which works on all pages) */"browser_action"{// If you have browser_action, add an icon to the right of the Chrome Toolbar,"default_icon": "test.jpg"."default_title": "Google Mail", // Tooltip displays when the cursor is hovering over the icon"default_popup": "popup.html"// If there is a popup page, the user clicks the icon to render the HTML page},"page_action"If page_action is not applied to the current page, the icon is grayed out"default_icon": {
     "19": "images/icon19.png"."38": "images/icon38.png"
   },
   "default_title": "Google Mail"."default_popup": "popup.html"} / / optional"author":"Developer"."automation":""."background": {"scripts": ["background.js"]."page": "background.html"."persistent":false}, /* Background web page 1. Applications often need a long-running script to manage some task or state, and background web pages are designed for this purpose. Typically, background pages do not require any HTML markup, in which case background pages can be implemented using JavaScript files alone. The background page will be generated by the application system and will contain each file listed in the scripts property. 2. Page: If you need to specify HTML in your background pages, you can use the Page attribute instead: 3. Persistent: Applications and applications often require long-running scripts to manage certain tasks or states, which is where event pages come in. Event pages are loaded only when needed and unloaded when they are inactive to free up memory and other system resources. How to get the event page is set up one"persistent"Key, if not set, you will get a normal background page. * /"content_scripts": [{
   "matches": ["https://*.domain.com/*"], // Match the address of the page"exclude_matches": []."js": ["jquery.js"."yourScript.js"], // Content script"css": ["yourStyles.css"], // Add CSS styles to the page"run_at":"document_idle"."all_frames": true// This matches all the Windows below},{// different content can be inserted for different rules"matches": ["*://*/*.png"."*://*/*.jpg"."*://*/*.gif"."*://*/*.bmp"]."js": ["js/show-image-content-size.js"Content scripts are JavaScript files that run in the context of the web page. They can use the standard Document Object Model (DOM) to retrieve details of the web page visited by the browser, or make changes to it. 1. Run_at optional. Controls when JavaScript files in JS are inserted"document_start","document_end""document_idle"By default,"document_idle". 1.1 if it is"document_start", these files will be inserted after the files specified in the CSS, but before all other DOM constructs or scripts are run. 1.2. If it is"document_end", the file is inserted immediately after the DOM is complete, but before child resources such as images and frames are loaded. 1.3. If it is"document_idle", the browser will be in"document_end"And immediately after the window.onload event. The exact insertion time depends on the complexity of the document and how long it takes to load it, and the browser is optimized to speed up page loading as much as possible. 2. All_frames optional. Controls whether the content script runs in all frames of the matching page or only in the top-level frame. The default isfalse, meaning that only the top-level framework runs */"web_accessible_resources": [// A list of plug-in resources that can be accessed directly from a normal page"images/*.png"."style/double-rainbow.css"."script/double-rainbow.js"."script/main.js"."templates/*"]."update_url": "Address of your plugin in chrome Store"If you want to update the plugin from your own server, you need to specify update_URL, which points to your server address."homepage_url": "https://www.xxx.com"// The home page of the plugin"permissions": ["tabs", // Add this item if the extension uses chrome. Tabs or chrome. Windows modules"bookmarks", // Use the Chrome. Bookmarks module to create, organize, and manage bookmarks"http://www.blogger.com/"."http://*.google.com/"."unlimitedStorage", // provides an unlimited HTML5 quota for storing client data such as databases and locally stored files. Without this permission, the expansion is limited to 5 MB of local storage"history"// History permission chrome.history"notifications"/ / hint"cookies",// Add this item if the extension uses the Chrome. cookies module,/ * A set of permissions that the extension or app will use. Each permission is one of a list of known strings, such as geolocatioin or a matching pattern, to specify one or more hosts that can be accessed. Permissions can help limit the risk if your extension or app is attacked. Some permissions are told */ key before installation:' ', /** specifies a unique identifier for the extension at development time. Note: You usually don't need to use this value directly, but instead use relative paths or absolute paths from Chrome.extension.geturl () in your code. This value is not explicitly specified at development time, but is generated by Chrome when installing.crx. (CRX files can be generated by uploading extensions or manually packaging during development). After installing CRX, you can see the extension key in the Default/Extensions/<extensionId>/<versionString>/manifest.json file in Chrome's user data directory. * * /"commands"// You need to add a listener binding handler on the background page"toggle-feature-foo": {
     "suggested_key": {
       "default": "Ctrl+Shift+Y"."mac": "Command+Shift+Y"
     },
     "description": "Toggle feature foo"."global": trueCtrl+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"], // Other permissions required when using the Chrome.permissions API, not when installing the plugin"short_name": "Short Name"// Short name of the plug-in"storage"// To use the storage.managed API, a schema file is required to specify the storage field type, similar to defining the column of a database table"managed_schema": "schema.json"},... }Copy the code

Well, once configured, you can insert your own scripts into the page and do whatever you want.

Here are the three problems we encountered during development:

1. Obtain the abbreviation code of the site. For example, the site code of [Beijing North] is VAP, because the parameters passed in the data request are not the Chinese name of the site, but the site code.

I found a variable in the website: station_names, as shown below:



It’s obvious to parse this variable to get the corresponding site code, but the Chrome plugin and the original web page are two separate runtime environments, which means I can’t get the variables in the page script in the plugin script. But plug-ins can get the dom content of the page, so you attach station_names to the DOM and then get the DOM properties in the plug-in. This gets the variable values from the page script using the DOM as follows:

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');Copy the code

2. Parse the data returned by 12306

Isn’t it easy to parse the data, you ask? I thought so, too, until I saw his return:



It’s impossible to parse the data by yourself, so I looked for the script to parse the data, so I found this function, and here it is:



After processing the above function, I get the result object I want:

Perfect!

3 originally thought that can be happy to play, but the next day to try, unexpectedly can not request the data.

If the request fails, it will return the available address. If the request fails, it will return the available address. If the request fails, it will return the available address.

let queryUrl = 'leftTicket/queryZ'// request address $.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:functionQueryUrl = res.responsejson.c_url}})Copy the code

Ok! To complete.

Conclusion:

1. If you find it useful, please give a star backhand

2. After boarding the train, please pay the ticket 😂

Thanks for watching