w-ipproxy 1.0.1 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.js +18 -17
- package/.github/workflows/ci-test.yml +3 -3
- package/README.md +64 -2
- package/babel.config.js +4 -2
- package/dist/w-ip-proxy.umd.js +7 -0
- package/dist/w-ip-proxy.umd.js.map +1 -0
- package/docs/WIpProxy.html +722 -0
- package/docs/WIpProxy.mjs.html +554 -0
- package/docs/fonts/Montserrat/Montserrat-Bold.eot +0 -0
- package/docs/fonts/Montserrat/Montserrat-Bold.ttf +0 -0
- package/docs/fonts/Montserrat/Montserrat-Bold.woff +0 -0
- package/docs/fonts/Montserrat/Montserrat-Bold.woff2 +0 -0
- package/docs/fonts/Montserrat/Montserrat-Regular.eot +0 -0
- package/docs/fonts/Montserrat/Montserrat-Regular.ttf +0 -0
- package/docs/fonts/Montserrat/Montserrat-Regular.woff +0 -0
- package/docs/fonts/Montserrat/Montserrat-Regular.woff2 +0 -0
- package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.eot +0 -0
- package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.svg +978 -0
- package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.ttf +0 -0
- package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.woff +0 -0
- package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.woff2 +0 -0
- package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.eot +0 -0
- package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.svg +1049 -0
- package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.ttf +0 -0
- package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.woff +0 -0
- package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.woff2 +0 -0
- package/docs/index.html +84 -0
- package/docs/scripts/collapse.js +39 -0
- package/docs/scripts/commonNav.js +28 -0
- package/docs/scripts/linenumber.js +25 -0
- package/docs/scripts/nav.js +12 -0
- package/docs/scripts/polyfill.js +4 -0
- package/docs/scripts/prettify/Apache-License-2.0.txt +202 -0
- package/docs/scripts/prettify/lang-css.js +2 -0
- package/docs/scripts/prettify/prettify.js +28 -0
- package/docs/scripts/search.js +99 -0
- package/docs/styles/jsdoc.css +776 -0
- package/docs/styles/prettify.css +80 -0
- package/g-getByTimer.mjs +24 -0
- package/g-getOnce.mjs +11 -0
- package/g-srv.mjs +28 -0
- package/package.json +11 -9
- package/script.txt +17 -0
- package/src/WIpProxy.mjs +473 -14
- package/src/provideServer.mjs +65 -0
- package/test/anonymity.test.mjs +140 -0
- package/test/getProxiesOnce.test.mjs +89 -0
- package/toolg/cleanFolder.mjs +4 -0
- package/toolg/gDistRollup.mjs +29 -0
- package/toolg/modifyReadme.mjs +4 -0
- package/g.mjs +0 -12
- package/src/getHtml.mjs +0 -84
- package/src/getProxyListOrg.mjs +0 -66
- package/src/parseHtml.mjs +0 -19
- package/test/all.test.mjs +0 -10
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import Hapi from '@hapi/hapi'
|
|
2
|
+
import get from 'lodash-es/get.js'
|
|
3
|
+
import ispint from 'wsemi/src/ispint.mjs'
|
|
4
|
+
import isearr from 'wsemi/src/isearr.mjs'
|
|
5
|
+
import isestr from 'wsemi/src/isestr.mjs'
|
|
6
|
+
import cint from 'wsemi/src/cint.mjs'
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
function provideServer(fun, opt = {}) {
|
|
10
|
+
|
|
11
|
+
//port
|
|
12
|
+
let port = get(opt, 'port')
|
|
13
|
+
if (!ispint(port)) {
|
|
14
|
+
port = 8080
|
|
15
|
+
}
|
|
16
|
+
port = cint(port)
|
|
17
|
+
|
|
18
|
+
//corsOrigins
|
|
19
|
+
let corsOrigins = get(opt, 'corsOrigins', [])
|
|
20
|
+
if (!isearr(corsOrigins)) {
|
|
21
|
+
corsOrigins = ['*']
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
//apiName
|
|
25
|
+
let apiName = get(opt, 'apiName')
|
|
26
|
+
if (!isestr(apiName)) {
|
|
27
|
+
apiName = 'getProxies'
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
//startServer
|
|
31
|
+
let startServer = async () => {
|
|
32
|
+
let server = Hapi.server({
|
|
33
|
+
// host: 'localhost',
|
|
34
|
+
port,
|
|
35
|
+
routes: {
|
|
36
|
+
cors: {
|
|
37
|
+
origin: corsOrigins, //Access-Control-Allow-Origin
|
|
38
|
+
credentials: false, //Access-Control-Allow-Credentials
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
//route
|
|
44
|
+
server.route({
|
|
45
|
+
method: 'GET',
|
|
46
|
+
path: `/${apiName}`,
|
|
47
|
+
handler: async () => {
|
|
48
|
+
let proxies = await fun()
|
|
49
|
+
return proxies
|
|
50
|
+
},
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
//start
|
|
54
|
+
await server.start()
|
|
55
|
+
|
|
56
|
+
console.log(`Server running at: ${server.info.uri}`)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
//startServer
|
|
60
|
+
startServer()
|
|
61
|
+
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
export default provideServer
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import assert from 'assert'
|
|
2
|
+
import axios from 'axios'
|
|
3
|
+
import https from 'https'
|
|
4
|
+
import wip from '../src/WIpProxy.mjs'
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
describe('anonymity (elite + anonymityCheck)', function() {
|
|
8
|
+
this.timeout(240 * 1000) //4min, 因為getProxiesOnce加上獨立leak驗證本身就耗時
|
|
9
|
+
|
|
10
|
+
let myIp = ''
|
|
11
|
+
let ps = []
|
|
12
|
+
|
|
13
|
+
before(async function() {
|
|
14
|
+
|
|
15
|
+
//取本機真實公網IP
|
|
16
|
+
try {
|
|
17
|
+
let r = await axios.get('https://httpbin.org/ip', { timeout: 10000 })
|
|
18
|
+
myIp = r.data?.origin || ''
|
|
19
|
+
}
|
|
20
|
+
catch (err) {
|
|
21
|
+
this.skip()
|
|
22
|
+
}
|
|
23
|
+
if (!myIp) {
|
|
24
|
+
this.skip()
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
//預設設定已是anonymity=elite且啟用anonymityCheck, 走完整過濾路徑
|
|
28
|
+
let wo = wip({})
|
|
29
|
+
ps = await wo.getProxiesOnce()
|
|
30
|
+
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
it('no returned proxy should leak real IP via httpbin origin', async function() {
|
|
34
|
+
if (ps.length === 0) {
|
|
35
|
+
this.skip()
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
let agent = new https.Agent({ rejectUnauthorized: false })
|
|
39
|
+
let sample = ps.slice(0, 30)
|
|
40
|
+
let pms = sample.map(async (p) => {
|
|
41
|
+
try {
|
|
42
|
+
let res = await axios.get('https://httpbin.org/ip', {
|
|
43
|
+
proxy: { host: p.host, port: p.port, protocol: 'http' },
|
|
44
|
+
httpsAgent: agent,
|
|
45
|
+
timeout: 8000,
|
|
46
|
+
headers: {
|
|
47
|
+
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/122.0.0.0 Safari/537.36',
|
|
48
|
+
'Accept': 'application/json',
|
|
49
|
+
},
|
|
50
|
+
validateStatus: () => true,
|
|
51
|
+
})
|
|
52
|
+
if (res.status !== 200) {
|
|
53
|
+
return { proxy: p.proxy, ok: true, reason: 'non-200, ignored' }
|
|
54
|
+
}
|
|
55
|
+
let origin = res.data?.origin
|
|
56
|
+
if (typeof origin !== 'string') {
|
|
57
|
+
return { proxy: p.proxy, ok: true, reason: 'non-json, ignored' }
|
|
58
|
+
}
|
|
59
|
+
//回傳origin含本機真實IP即代表該代理洩漏
|
|
60
|
+
let leaks = origin.indexOf(myIp) >= 0
|
|
61
|
+
return { proxy: p.proxy, ok: !leaks, origin }
|
|
62
|
+
}
|
|
63
|
+
catch (err) {
|
|
64
|
+
//網路錯誤不算洩漏, 直接放行
|
|
65
|
+
return { proxy: p.proxy, ok: true, reason: err.message }
|
|
66
|
+
}
|
|
67
|
+
})
|
|
68
|
+
let rrs = await Promise.all(pms)
|
|
69
|
+
let leaked = rrs.filter((r) => !r.ok)
|
|
70
|
+
|
|
71
|
+
assert.strictEqual(
|
|
72
|
+
leaked.length, 0,
|
|
73
|
+
`expected 0 proxies to leak real IP, got ${leaked.length}: ` +
|
|
74
|
+
leaked.slice(0, 5).map((r) => `${r.proxy} (origin=${r.origin})`).join('; ')
|
|
75
|
+
)
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
it('no returned proxy should carry X-Forwarded-For / Via when viewed from target', async function() {
|
|
79
|
+
if (ps.length === 0) {
|
|
80
|
+
this.skip()
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
let LEAK_IP_HEADERS = [
|
|
84
|
+
'x-forwarded-for', 'x-real-ip', 'x-client-ip', 'client-ip',
|
|
85
|
+
'true-client-ip', 'cf-connecting-ip', 'forwarded',
|
|
86
|
+
'x-originating-ip', 'x-cluster-client-ip',
|
|
87
|
+
]
|
|
88
|
+
let LEAK_PROXY_HEADERS = [
|
|
89
|
+
'via', 'proxy-connection', 'x-proxy-id',
|
|
90
|
+
]
|
|
91
|
+
|
|
92
|
+
let agent = new https.Agent({ rejectUnauthorized: false })
|
|
93
|
+
let sample = ps.slice(0, 30)
|
|
94
|
+
let pms = sample.map(async (p) => {
|
|
95
|
+
try {
|
|
96
|
+
let res = await axios.get('https://httpbin.org/headers', {
|
|
97
|
+
proxy: { host: p.host, port: p.port, protocol: 'http' },
|
|
98
|
+
httpsAgent: agent,
|
|
99
|
+
timeout: 8000,
|
|
100
|
+
headers: {
|
|
101
|
+
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/122.0.0.0 Safari/537.36',
|
|
102
|
+
'Accept': 'application/json',
|
|
103
|
+
},
|
|
104
|
+
validateStatus: () => true,
|
|
105
|
+
})
|
|
106
|
+
if (res.status !== 200 || typeof res.data?.headers !== 'object') {
|
|
107
|
+
return { proxy: p.proxy, ok: true, reason: 'ignored' }
|
|
108
|
+
}
|
|
109
|
+
let hs = {}
|
|
110
|
+
for (let k in res.data.headers) {
|
|
111
|
+
hs[k.toLowerCase()] = String(res.data.headers[k])
|
|
112
|
+
}
|
|
113
|
+
let bad = []
|
|
114
|
+
for (let k of LEAK_IP_HEADERS) {
|
|
115
|
+
if (k in hs && hs[k].indexOf(myIp) >= 0) {
|
|
116
|
+
bad.push(`${k}=${hs[k]}`)
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
for (let k of LEAK_PROXY_HEADERS) {
|
|
120
|
+
if (k in hs) {
|
|
121
|
+
bad.push(`${k}=${hs[k]}`)
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return { proxy: p.proxy, ok: bad.length === 0, bad }
|
|
125
|
+
}
|
|
126
|
+
catch (err) {
|
|
127
|
+
return { proxy: p.proxy, ok: true, reason: err.message }
|
|
128
|
+
}
|
|
129
|
+
})
|
|
130
|
+
let rrs = await Promise.all(pms)
|
|
131
|
+
let leaked = rrs.filter((r) => !r.ok)
|
|
132
|
+
|
|
133
|
+
assert.strictEqual(
|
|
134
|
+
leaked.length, 0,
|
|
135
|
+
`expected 0 proxies with leaky headers, got ${leaked.length}: ` +
|
|
136
|
+
leaked.slice(0, 5).map((r) => `${r.proxy} (${r.bad.join(',')})`).join('; ')
|
|
137
|
+
)
|
|
138
|
+
})
|
|
139
|
+
|
|
140
|
+
})
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import assert from 'assert'
|
|
2
|
+
import axios from 'axios'
|
|
3
|
+
import https from 'https'
|
|
4
|
+
import wip from '../src/WIpProxy.mjs'
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
describe('getProxiesOnce', function() {
|
|
8
|
+
this.timeout(180 * 1000) //3min, 因為需要抓清單+檢測+獨立驗證
|
|
9
|
+
|
|
10
|
+
let myIp = ''
|
|
11
|
+
let ps = []
|
|
12
|
+
|
|
13
|
+
before(async function() {
|
|
14
|
+
|
|
15
|
+
//取本機真實公網IP (不走代理), 作為後續洩漏比對基準
|
|
16
|
+
try {
|
|
17
|
+
let r = await axios.get('https://httpbin.org/ip', { timeout: 10000 })
|
|
18
|
+
myIp = r.data?.origin || ''
|
|
19
|
+
}
|
|
20
|
+
catch (err) {
|
|
21
|
+
//網路異常則整組test跳過
|
|
22
|
+
this.skip()
|
|
23
|
+
}
|
|
24
|
+
if (!myIp) {
|
|
25
|
+
this.skip()
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
//取代理清單 (預設anonymity=elite + 匿名性檢查)
|
|
29
|
+
let wo = wip({})
|
|
30
|
+
ps = await wo.getProxiesOnce()
|
|
31
|
+
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
it('should return a non-empty array of proxy objects', function() {
|
|
35
|
+
//若上游proxyscrape當下無代理供應, 放行以免CI誤報 (非本專案bug)
|
|
36
|
+
if (ps.length === 0) {
|
|
37
|
+
this.skip()
|
|
38
|
+
}
|
|
39
|
+
assert.ok(Array.isArray(ps))
|
|
40
|
+
for (let p of ps) {
|
|
41
|
+
assert.strictEqual(typeof p.host, 'string')
|
|
42
|
+
assert.ok(p.host.length > 0)
|
|
43
|
+
assert.strictEqual(typeof p.port, 'number')
|
|
44
|
+
assert.ok(p.port > 0)
|
|
45
|
+
assert.strictEqual(p.proxy, `${p.host}:${p.port}`)
|
|
46
|
+
}
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
it('majority of returned proxies should be independently usable against httpbin', async function() {
|
|
50
|
+
if (ps.length === 0) {
|
|
51
|
+
this.skip()
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
//獨立再打一次httpbin.org/ip, 不倚賴內建的檢測結果
|
|
55
|
+
let agent = new https.Agent({ rejectUnauthorized: false })
|
|
56
|
+
let sample = ps.slice(0, 30) //取前30個避免長時間阻塞CI
|
|
57
|
+
let pms = sample.map(async (p) => {
|
|
58
|
+
try {
|
|
59
|
+
let res = await axios.get('https://httpbin.org/ip', {
|
|
60
|
+
proxy: { host: p.host, port: p.port, protocol: 'http' },
|
|
61
|
+
httpsAgent: agent,
|
|
62
|
+
timeout: 8000,
|
|
63
|
+
headers: {
|
|
64
|
+
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/122.0.0.0 Safari/537.36',
|
|
65
|
+
'Accept': 'application/json',
|
|
66
|
+
},
|
|
67
|
+
validateStatus: () => true,
|
|
68
|
+
})
|
|
69
|
+
return res.status === 200
|
|
70
|
+
&& typeof res.data === 'object'
|
|
71
|
+
&& res.data !== null
|
|
72
|
+
&& typeof res.data.origin === 'string'
|
|
73
|
+
}
|
|
74
|
+
catch (err) {
|
|
75
|
+
return false
|
|
76
|
+
}
|
|
77
|
+
})
|
|
78
|
+
let rrs = await Promise.all(pms)
|
|
79
|
+
let okCount = rrs.filter((v) => v).length
|
|
80
|
+
let ratio = okCount / rrs.length
|
|
81
|
+
|
|
82
|
+
//要求至少50%仍可用, 免疫於個別代理剛好在測試間掛掉的flakiness
|
|
83
|
+
assert.ok(
|
|
84
|
+
ratio >= 0.5,
|
|
85
|
+
`independent reachability ratio too low: ${okCount}/${rrs.length} (${(ratio * 100).toFixed(1)}%)`
|
|
86
|
+
)
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
})
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import rollupFiles from 'w-package-tools/src/rollupFiles.mjs'
|
|
2
|
+
import getFiles from 'w-package-tools/src/getFiles.mjs'
|
|
3
|
+
import w from 'wsemi'
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
let fdSrc = './src'
|
|
7
|
+
let fdTar = './dist'
|
|
8
|
+
let fns = getFiles(fdSrc)
|
|
9
|
+
fns = fns.filter((v) => {
|
|
10
|
+
return w.strleft(v, 1) === 'W'
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
rollupFiles({
|
|
14
|
+
fns,
|
|
15
|
+
fdSrc,
|
|
16
|
+
fdTar,
|
|
17
|
+
nameDistType: 'kebabCase',
|
|
18
|
+
globals: {
|
|
19
|
+
'@hapi/hapi': '@hapi/hapi',
|
|
20
|
+
'@hapi/inert': '@hapi/inert',
|
|
21
|
+
'https': 'https',
|
|
22
|
+
},
|
|
23
|
+
external: [
|
|
24
|
+
'@hapi/hapi',
|
|
25
|
+
'@hapi/inert',
|
|
26
|
+
'https',
|
|
27
|
+
],
|
|
28
|
+
})
|
|
29
|
+
|
package/g.mjs
DELETED
package/src/getHtml.mjs
DELETED
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
import puppeteer from 'puppeteer'
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
async function getHtml(url, selector, muti = false) {
|
|
5
|
-
|
|
6
|
-
async function view(url) {
|
|
7
|
-
|
|
8
|
-
//new page
|
|
9
|
-
let olaunch = {
|
|
10
|
-
//timeout: 0,
|
|
11
|
-
headless: false,
|
|
12
|
-
slowMo: 20,
|
|
13
|
-
// arg: [
|
|
14
|
-
// //`--window-size=${viewport.width},${viewport.height}`, //無效
|
|
15
|
-
// //`--disable-resize-lock`, //無效
|
|
16
|
-
// ],
|
|
17
|
-
// defaultViewport: { //無效
|
|
18
|
-
// x: 0,
|
|
19
|
-
// y: 0,
|
|
20
|
-
// width: 800,
|
|
21
|
-
// height: 600,
|
|
22
|
-
// }
|
|
23
|
-
}
|
|
24
|
-
let browser = await puppeteer.launch(olaunch)
|
|
25
|
-
let page = await browser.newPage()
|
|
26
|
-
|
|
27
|
-
//show page
|
|
28
|
-
await page.goto(url)
|
|
29
|
-
let viewport = {
|
|
30
|
-
x: 0,
|
|
31
|
-
y: 0,
|
|
32
|
-
width: 800,
|
|
33
|
-
height: 600,
|
|
34
|
-
}
|
|
35
|
-
await page.setViewport(viewport)
|
|
36
|
-
|
|
37
|
-
//wait
|
|
38
|
-
let waitsec = 5
|
|
39
|
-
await page.waitFor(waitsec * 1000)
|
|
40
|
-
|
|
41
|
-
return {
|
|
42
|
-
browser,
|
|
43
|
-
page,
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
async function close(p) {
|
|
48
|
-
|
|
49
|
-
//close
|
|
50
|
-
await p.page.close()
|
|
51
|
-
await p.browser.close()
|
|
52
|
-
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
async function g1(p, selector) {
|
|
56
|
-
return await p.page.$eval(selector, (node) => {
|
|
57
|
-
return node.innerHTML
|
|
58
|
-
})
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
async function gs(p, selector) {
|
|
62
|
-
return await p.page.$$eval(selector, (nodes) => {
|
|
63
|
-
return nodes.map((node) => {
|
|
64
|
-
return node.innerHTML
|
|
65
|
-
})
|
|
66
|
-
})
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
let p = await view(url)
|
|
70
|
-
|
|
71
|
-
let r
|
|
72
|
-
if (muti) {
|
|
73
|
-
r = await gs(p, selector)
|
|
74
|
-
}
|
|
75
|
-
else {
|
|
76
|
-
r = await g1(p, selector)
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
close(p)
|
|
80
|
-
|
|
81
|
-
return r
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
export default getHtml
|
package/src/getProxyListOrg.mjs
DELETED
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
// import fs from 'fs'
|
|
2
|
-
import get from 'lodash/get'
|
|
3
|
-
import sep from 'wsemi/src/sep.mjs'
|
|
4
|
-
import pmMap from 'wsemi/src/pmMap.mjs'
|
|
5
|
-
import parseHtml from './parseHtml.mjs'
|
|
6
|
-
import getHtml from './getHtml.mjs'
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
async function core(url) {
|
|
10
|
-
let ps = []
|
|
11
|
-
|
|
12
|
-
let selector = 'div.table'
|
|
13
|
-
let h = await getHtml(url, selector)
|
|
14
|
-
// fs.writeFileSync('temp.html', h)
|
|
15
|
-
// let h = fs.readFileSync('tempProxyListOrg.html', 'utf8')
|
|
16
|
-
|
|
17
|
-
//parseHtml
|
|
18
|
-
let $ = parseHtml(h)
|
|
19
|
-
$('ul').map((k, v) => {
|
|
20
|
-
let q = $(v)
|
|
21
|
-
|
|
22
|
-
let proxy = q.children('li.proxy').html()
|
|
23
|
-
proxy = sep(proxy, 'script>')
|
|
24
|
-
proxy = get(proxy, 1)
|
|
25
|
-
// console.log(k,'proxy',proxy)
|
|
26
|
-
|
|
27
|
-
let httpType = q.children('li.https').text()
|
|
28
|
-
// console.log(k,'httpType',httpType)
|
|
29
|
-
|
|
30
|
-
let country = q.find('span.country').text()
|
|
31
|
-
// console.log(k,'country',country)
|
|
32
|
-
|
|
33
|
-
let countryName = q.find('span.name').attr('name')
|
|
34
|
-
// console.log(k,'countryName',countryName)
|
|
35
|
-
|
|
36
|
-
ps.push({
|
|
37
|
-
proxy,
|
|
38
|
-
httpType,
|
|
39
|
-
country,
|
|
40
|
-
countryName,
|
|
41
|
-
})
|
|
42
|
-
})
|
|
43
|
-
|
|
44
|
-
return ps
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
async function getProxyListOrg() {
|
|
48
|
-
let psAll = []
|
|
49
|
-
|
|
50
|
-
await pmMap([1, 2, 3], async(v) => {
|
|
51
|
-
// console.log(v)
|
|
52
|
-
|
|
53
|
-
//url
|
|
54
|
-
//let url = 'https://proxy-list.org/chinese/index.php?setlang=chinese'
|
|
55
|
-
let url = `https://proxy-list.org/chinese/index.php?setlang=chinese&p=${v}`
|
|
56
|
-
|
|
57
|
-
//core
|
|
58
|
-
let ps = await core(url)
|
|
59
|
-
psAll = [...psAll, ...ps]
|
|
60
|
-
|
|
61
|
-
})
|
|
62
|
-
|
|
63
|
-
return psAll
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
export default getProxyListOrg
|
package/src/parseHtml.mjs
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
import cheerio from 'cheerio'
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
function parseHtml(h) {
|
|
6
|
-
|
|
7
|
-
let $setting = {
|
|
8
|
-
//ignoreWhitespace: true,
|
|
9
|
-
xmlMode: true,
|
|
10
|
-
decodeEntities: false,
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
//$
|
|
14
|
-
let $ = cheerio.load(h, $setting)
|
|
15
|
-
|
|
16
|
-
return $
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export default parseHtml
|