├── .gitattributes ├── .github └── workflows │ ├── main.yml │ └── release.yaml ├── LICENSE ├── Makefile ├── README.md ├── go.mod ├── go.sum ├── iptest-txt ├── README.md ├── downip.py ├── formatip.sh ├── ipspeedtest.sh ├── run.bat ├── tools │ ├── iptest.exe │ └── locations.json └── uploadcsv.sh ├── iptest.go代码 ├── locations.json └── main.go /.gitattributes: -------------------------------------------------------------------------------- 1 | locations.json 2 | ip.csv 3 | ip.txt 4 | .idea 5 | /iptest 6 | /tmp 7 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: GitHub CI 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | paths: 7 | - 'main.go' # 仅当 main.go 文件发生变化时触发 8 | 9 | concurrency: 10 | group: ${{ github.ref }}-${{ github.workflow }} 11 | cancel-in-progress: true 12 | 13 | jobs: 14 | build-go-binary: 15 | runs-on: ubuntu-latest 16 | strategy: 17 | matrix: 18 | goos: [linux, windows, darwin] 19 | goarch: [amd64, arm64, arm, 386] 20 | exclude: 21 | - goarch: arm64 22 | goos: windows 23 | - goarch: 386  24 | goos: darwin  25 | # arm 排除 26 | - goarch: arm 27 | goos: windows 28 | - goarch: arm 29 | goos: darwin 30 | 31 | steps: 32 | - uses: actions/checkout@v3 33 | - name: Get latest tag 34 | id: get_latest_tag 35 | run: | 36 | DEFAULT_TAG="v1.0.3" # 默认值 37 | # 检查是否存在 Tag,如果不存在则使用默认值 38 | if git tag --list | grep -q .; then 39 | LATEST_TAG=$(git describe --tags --abbrev=0) 40 | else 41 | LATEST_TAG="$DEFAULT_TAG" 42 | fi 43 | echo "Latest tag: $LATEST_TAG" 44 | echo "LATEST_TAG=$LATEST_TAG" >> $GITHUB_ENV 45 | - name: Generate new version 46 | id: generate_version 47 | run: | 48 | LATEST_TAG=${{ env.LATEST_TAG }} 49 | if [[ -z "$LATEST_TAG" ]]; then 50 | # 如果没有 Tag,使用默认值 51 | LATEST_TAG="$DEFAULT_TAG" 52 | fi 53 | # 提取主版本号、次版本号和修订号 54 | MAJOR=$(echo "$LATEST_TAG" | cut -d. -f1 | tr -d 'v') 55 | MINOR=$(echo "$LATEST_TAG" | cut -d. -f2) 56 | PATCH=$(echo "$LATEST_TAG" | cut -d. -f3) 57 | # 增加修订号 58 | NEW_PATCH=$((PATCH + 1)) 59 | # 生成新版本号 60 | NEW_VERSION="v${MAJOR}.${MINOR}.${NEW_PATCH}" 61 | echo "New version: $NEW_VERSION" 62 | echo "NEW_VERSION=$NEW_VERSION" >> $GITHUB_ENV 63 | - uses: wangyoucao577/go-release-action@v1 64 | with: 65 | github_token: ${{ secrets.GITHUB_TOKEN }} # 一个默认的变量,用来实现往 Release 中添加文件 66 | goos: ${{ matrix.goos }} 67 | goarch: ${{ matrix.goarch }} 68 | goversion: 1.21 # 可以指定编译使用的 Golang 版本 69 | binary_name: "iptest" # 使用固定的二进制文件名 70 | ldflags: -s -w -X "main.version=${{ env.NEW_VERSION }}" # 将版本号注入到 Go 二进制文件 71 | md5sum: false 72 | release_tag: ${{ env.NEW_VERSION }} # 使用新版本号作为 Release 的 Tag 73 | create_release: true # 自动创建 Release 74 | asset_name: "iptest-${{ matrix.goos }}-${{ matrix.goarch }}.zip" # 自定义 Release 文件名为 ZIP 格式 75 | compress_assets: true # 启用压缩功能 76 | -------------------------------------------------------------------------------- /.github/workflows/release.yaml: -------------------------------------------------------------------------------- 1 | name: build 2 | 3 | on: 4 | push: 5 | tags: 6 | - 'v*.*.*' # 匹配以 v 开头,后跟数字的 Tag,例如 v1.0.0 7 | 8 | permissions: 9 | contents: write 10 | packages: write 11 | 12 | jobs: 13 | build-go-binary: 14 | runs-on: ubuntu-latest 15 | strategy: 16 | matrix: 17 | goos: [linux, windows, darwin] # 需要打包的系统 18 | goarch: [amd64, arm64, arm, 386] # 需要打包的架构 19 | exclude: # 排除某些平台和架构 20 | - goarch: arm64 21 | goos: windows 22 | - goarch: 386 23 | goos: darwin 24 | # arm 排除 25 | - goarch: arm 26 | goos: windows 27 | - goarch: arm 28 | goos: darwin 29 | 30 | steps: 31 | - uses: actions/checkout@v3 32 | - uses: wangyoucao577/go-release-action@v1 33 | with: 34 | github_token: ${{ secrets.GITHUB_TOKEN }} # 一个默认的变量,用来实现往 Release 中添加文件 35 | goos: ${{ matrix.goos }} 36 | goarch: ${{ matrix.goarch }} 37 | goversion: 1.21 # 可以指定编译使用的 Golang 版本 38 | binary_name: "iptest" # 使用固定的二进制文件名 39 | ldflags: -s -w -X "main.version=${{ github.ref_name }}" # 将版本号注入到 Go 二进制文件 40 | md5sum: false 41 | release_tag: ${{ github.ref_name }} # 使用 Tag 作为 Release 的版本号 42 | create_release: true # 自动创建 Release 43 | asset_name: "iptest-${{ matrix.goos }}-${{ matrix.goarch }}.zip" # 自定义 Release 文件名为 ZIP 格式 44 | compress_assets: true # 启用压缩功能 45 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 badafans 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | NAME=CFiptest 2 | BINDIR=bin 3 | VERSION=$(shell git describe --tags || echo "unknown version") 4 | GOBUILD=CGO_ENABLED=0 go build -trimpath -ldflags '-w -s' 5 | 6 | PLATFORM_LIST = \ 7 | darwin-amd64 \ 8 | windows-386 \ 9 | windows-amd64 \ 10 | linux-386 \ 11 | linux-amd64 \ 12 | linux-armv5 \ 13 | linux-armv6 \ 14 | linux-armv7 \ 15 | linux-armv8 \ 16 | linux-arm64 \ 17 | linux-mips-softfloat \ 18 | linux-mips-hardfloat \ 19 | linux-mipsle-softfloat \ 20 | linux-mipsle-hardfloat \ 21 | linux-mips64 \ 22 | linux-mips64le \ 23 | freebsd-386 \ 24 | freebsd-amd64 25 | 26 | 27 | 28 | all: darwin-amd64 windows-amd64 linux-386 linux-amd64 linux-armv5 linux-armv6 linux-armv7 linux-armv8 linux-arm64 linux-mips-softfloat linux-mips-hardfloat linux-mipsle-softfloat linux-mipsle-hardfloat linux-mips64 linux-mips64le freebsd-386 freebsd-amd64 29 | 30 | darwin-amd64: 31 | GOARCH=amd64 GOOS=darwin $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ 32 | 33 | 34 | windows-amd64: 35 | GOARCH=amd64 GOOS=windows $(GOBUILD) -o $(BINDIR)/$(NAME)-$@.exe 36 | 37 | linux-386: 38 | GOARCH=386 GOOS=linux $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ 39 | 40 | linux-amd64: 41 | GOARCH=amd64 GOOS=linux $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ 42 | 43 | linux-armv5: 44 | GOARCH=arm GOOS=linux GOARM=5 $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ 45 | 46 | linux-armv6: 47 | GOARCH=arm GOOS=linux GOARM=6 $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ 48 | 49 | linux-armv7: 50 | GOARCH=arm GOOS=linux GOARM=7 $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ 51 | 52 | linux-armv8: 53 | GOARCH=arm64 GOOS=linux $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ 54 | 55 | linux-arm64: 56 | GOARCH=arm64 GOOS=linux $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ 57 | 58 | linux-mips-softfloat: 59 | GOARCH=mips GOMIPS=softfloat GOOS=linux $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ 60 | 61 | linux-mips-hardfloat: 62 | GOARCH=mips GOMIPS=hardfloat GOOS=linux $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ 63 | 64 | linux-mipsle-softfloat: 65 | GOARCH=mipsle GOMIPS=softfloat GOOS=linux $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ 66 | 67 | linux-mipsle-hardfloat: 68 | GOARCH=mipsle GOMIPS=hardfloat GOOS=linux $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ 69 | 70 | linux-mips64: 71 | GOARCH=mips64 GOOS=linux $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ 72 | 73 | linux-mips64le: 74 | GOARCH=mips64le GOOS=linux $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ 75 | 76 | freebsd-386: 77 | GOARCH=386 GOOS=freebsd $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ 78 | 79 | freebsd-amd64: 80 | GOARCH=amd64 GOOS=freebsd $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ 81 | 82 | gz_releases=$(addsuffix .gz, $(PLATFORM_LIST)) 83 | 84 | $(gz_releases): %.gz : % 85 | chmod +x $(BINDIR)/$(NAME)-$(basename $@) 86 | gzip -f -S -$(VERSION).gz $(BINDIR)/$(NAME)-$(basename $@) 87 | 88 | all-arch: $(PLATFORM_LIST) 89 | 90 | releases: $(gz_releases) 91 | clean: 92 | rm $(BINDIR)/* 93 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 修改说明 2 | 在原版基础上增加了 delay 延迟参数,默认值为 220ms,超过该延迟的 ip 将跳过测速 3 | 4 | ## 项目简介 5 | 这是一个用于测试 IP 地址延迟和下载速度的工具。它可以从指定的文件中读取 IP 地址和端口,并发进行 TCP 连接测试和 HTTP 请求测试,筛选低于延迟阈值的 IP 并进行下载速度测试,也可通过参数设置仅测延迟不测速。最终将结果写入 CSV 文件。 6 | 7 | ## 核心功能 8 | - **高并发支持**:可设置并发请求的最大协程数,提高检测效率。 9 | - **数据中心位置识别**:通过 Cloudflare 的 cdn-cgi/trace 接口获取数据中心位置信息。 10 | - **延迟阈值**:支持对 IP 进行延迟测试,并筛选低于默认延迟阈值的数据。(**在原版基础上新增的功能**) 11 | - **下载速度测试**:支持对符合条件的 IP 地址进行下载速度测试。 12 | - **结果导出**:将测试结果导出为 CSV 文件,包含 IP 地址、端口、数据中心、地区、国家、城市、延迟和下载速度等信息(全中文化)。 13 | 14 | ## 安装说明 15 | ### 自行编译 16 | 确保你已经安装了 Go 环境,然后使用以下命令编译程序: 17 | 18 | ```bash 19 | git clone https://github.com/yutian81/IP-SpeedTest.git 20 | cd IP-SpeedTest 21 | go build -o iptest main.go 22 | ``` 23 | 24 | ### 下载编译好的软件 25 | 根据系统和CPU架构选择: 26 | https://github.com/yutian81/IP-SpeedTest/releases 27 | 28 | ## 使用说明 29 | ### 全参数运行示例 30 | ```bash 31 | ./iptest -file=ip.txt -outfile=ip.csv -max=100 -speedtest=5 -speedlimit=10 -delay=220 -url=speed.cloudflare.com/__down?bytes=500000000 -tls=true -tcpurl=www.speedtest.net 32 | ``` 33 | 34 | ### 推荐运行示例 35 | ```bash 36 | ./iptest -file=ip.txt -outfile=ip.csv -max=300 -speedtest=3 -speedlimit=5 -delay=200 -url=spurl.api.030101.xyz/50mb 37 | ``` 38 | 39 | ### 参数说明 40 | | 参数 | 描述 | 默认值 | 41 | | ---- | ---- | ----- | 42 | -file | IP 地址文件名称,格式为 ip port,IP 和端口之间用空格隔开 | ip.txt | 43 | -outfile | 输出文件名称 | ip.csv | 44 | -max | 并发请求最大协程数 | 100 | 45 | -delay | 延迟阈值(ms) | 220 | 46 | -speedtest | 下载测速协程数量,设为 0 禁用测速 | 5 | 47 | -speedlimit | 最低下载速度(MB/s),低于该速度不写入结果 csv 文件 | 0 | 48 | -url | 测速文件地址,不需要http或https协议头 | speed.cloudflare.com/__down?bytes=500000000 | 49 | -tls | 是否启用 TLS | true | 50 | -tcpurl | TCP 请求地址,不需要http或https协议头 | www.speedtest.net | 51 | 52 | ### 关于 -file 53 | **-file**:指定包含 IP 地址和端口的文件路径,默认为 `ip.txt`。 54 | 文件格式为每行一个 `IP 地址和端口`,用`空格`分隔,例如: 55 | ``` 56 | 1.1.1.1 80 57 | 2.2.2.2 443 58 | 3.3.3.3 8080 59 | ``` 60 | 61 | ### 关于 -outfile 62 | **-outfile**:测速后输出的 csv 文件,默认为 `ip.csv`。 63 | 输出内容已全部`中文化`(内置在代码中,不依赖外部 `location.json` 文件 )示例输出: 64 | ```csv 65 | IP地址,端口,TLS,数据中心,地区,国际代码,国家,城市,网络延迟,下载速度MB/s 66 | 192.168.1.1,443,true,LAX,北美洲,US,美国,洛杉矶,50 ms,12.34 67 | 192.168.1.2,80,false,SIN,亚太,SG,新加坡,新加坡,100 ms,8.76 68 | ``` 69 | 70 | ## 注意事项 71 | - 文件描述符限制:在 Linux 系统上,如果以 root 用户运行,程序会尝试提升文件描述符的上限。如果你遇到文件描述符不足的问题,请确保以 root 用户运行程序。 72 | - 测速文件:默认使用的是 Cloudflare 500m 的测速文件,为了提高测速效率,可以使用其他测速地址,如mingyu大佬的`spurl.api.030101.xyz/50mb`。 73 | - 延迟阈值:通过 `-delay` 参数可以过滤掉延迟较高的 IP 地址,确保只测试低延迟的 IP。 74 | 75 | ## 依赖 76 | Go 1.16 或更高版本。 77 | 78 | ## 许可证 79 | 本项目基于 MIT 许可证开源。 80 | 本软件按 "原样" 提供,没有任何形式的明示或暗示保证,包括但不限于适销性保证、特定用途适用性保证和非侵权保证。在任何情况下,作者或版权所有者均不对任何索赔、损害或其他责任负责,无论是在合同、侵权或其他方面,由于或与软件或使用或其他交易中的软件产生或与之相关的操作。 81 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bh-qt/Cloudflare-IP-SpeedTest 2 | 3 | go 1.22.1 4 | 5 | require github.com/PuerkitoBio/goquery v1.9.1 6 | 7 | require ( 8 | github.com/francoispqt/gojay v1.2.13 // indirect 9 | github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect 10 | github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect 11 | github.com/onsi/ginkgo/v2 v2.9.5 // indirect 12 | github.com/quic-go/qpack v0.4.0 // indirect 13 | go.uber.org/mock v0.4.0 // indirect 14 | golang.org/x/crypto v0.21.0 // indirect 15 | golang.org/x/exp v0.0.0-20221205204356-47842c84f3db // indirect 16 | golang.org/x/mod v0.11.0 // indirect 17 | golang.org/x/sys v0.18.0 // indirect 18 | golang.org/x/text v0.14.0 // indirect 19 | golang.org/x/tools v0.9.1 // indirect 20 | ) 21 | 22 | require ( 23 | github.com/andybalholm/cascadia v1.3.2 // indirect 24 | github.com/quic-go/quic-go v0.43.1 25 | golang.org/x/net v0.23.0 // indirect 26 | ) 27 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | cloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 3 | cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 4 | cloud.google.com/go v0.37.0/go.mod h1:TS1dMSSfndXH133OKGwekG838Om/cQT0BUHV3HcBgoo= 5 | dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU= 6 | dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU= 7 | dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4= 8 | dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU= 9 | git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg= 10 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 11 | github.com/PuerkitoBio/goquery v1.9.1 h1:mTL6XjbJTZdpfL+Gwl5U2h1l9yEkJjhmlTeV9VPW7UI= 12 | github.com/PuerkitoBio/goquery v1.9.1/go.mod h1:cW1n6TmIMDoORQU5IU/P1T3tGFunOeXEpGP2WHRwkbY= 13 | github.com/andybalholm/cascadia v1.3.2 h1:3Xi6Dw5lHF15JtdcmAHD3i1+T8plmv7BQ/nsViSLyss= 14 | github.com/andybalholm/cascadia v1.3.2/go.mod h1:7gtRlve5FxPPgIgX36uWBX58OdBsSS6lUvCFb+h7KvU= 15 | github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= 16 | github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= 17 | github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= 18 | github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= 19 | github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= 20 | github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= 21 | github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= 22 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 23 | github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= 24 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 25 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 26 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 27 | github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= 28 | github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= 29 | github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk= 30 | github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY= 31 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 32 | github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= 33 | github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= 34 | github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= 35 | github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= 36 | github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= 37 | github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= 38 | github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= 39 | github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= 40 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 41 | github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= 42 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 43 | github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 44 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 45 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 46 | github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= 47 | github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= 48 | github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 49 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 50 | github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= 51 | github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 52 | github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= 53 | github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= 54 | github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= 55 | github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 56 | github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE= 57 | github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 58 | github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= 59 | github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg= 60 | github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= 61 | github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= 62 | github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= 63 | github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= 64 | github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU= 65 | github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= 66 | github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= 67 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 68 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 69 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 70 | github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 71 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 72 | github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= 73 | github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= 74 | github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= 75 | github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= 76 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 77 | github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 78 | github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= 79 | github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= 80 | github.com/onsi/ginkgo/v2 v2.9.5 h1:+6Hr4uxzP4XIUyAkg61dWBw8lb/gc4/X5luuxN/EC+Q= 81 | github.com/onsi/ginkgo/v2 v2.9.5/go.mod h1:tvAoo1QUJwNEU2ITftXTpR7R1RbCzoZUOs3RonqW57k= 82 | github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE= 83 | github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg= 84 | github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= 85 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 86 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 87 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 88 | github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= 89 | github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= 90 | github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= 91 | github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= 92 | github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo= 93 | github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A= 94 | github.com/quic-go/quic-go v0.43.1 h1:fLiMNfQVe9q2JvSsiXo4fXOEguXHGGl9+6gLp4RPeZQ= 95 | github.com/quic-go/quic-go v0.43.1/go.mod h1:132kz4kL3F9vxhW3CtQJLDVwcFe5wdWeJXXijhsO57M= 96 | github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= 97 | github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= 98 | github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY= 99 | github.com/shurcooL/events v0.0.0-20181021180414-410e4ca65f48/go.mod h1:5u70Mqkb5O5cxEA8nxTsgrgLehJeAw6Oc4Ab1c/P1HM= 100 | github.com/shurcooL/github_flavored_markdown v0.0.0-20181002035957-2122de532470/go.mod h1:2dOwnU2uBioM+SGy2aZoq1f/Sd1l9OkAeAUvjSyvgU0= 101 | github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= 102 | github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ= 103 | github.com/shurcooL/gofontwoff v0.0.0-20180329035133-29b52fc0a18d/go.mod h1:05UtEgK5zq39gLST6uB0cf3NEHjETfB4Fgr3Gx5R9Vw= 104 | github.com/shurcooL/gopherjslib v0.0.0-20160914041154-feb6d3990c2c/go.mod h1:8d3azKNyqcHP1GaQE/c6dDgjkgSx2BZ4IoEi4F1reUI= 105 | github.com/shurcooL/highlight_diff v0.0.0-20170515013008-09bb4053de1b/go.mod h1:ZpfEhSmds4ytuByIcDnOLkTHGUI6KNqRNPDLHDk+mUU= 106 | github.com/shurcooL/highlight_go v0.0.0-20181028180052-98c3abbbae20/go.mod h1:UDKB5a1T23gOMUJrI+uSuH0VRDStOiUVSjBTRDVBVag= 107 | github.com/shurcooL/home v0.0.0-20181020052607-80b7ffcb30f9/go.mod h1:+rgNQw2P9ARFAs37qieuu7ohDNQ3gds9msbT2yn85sg= 108 | github.com/shurcooL/htmlg v0.0.0-20170918183704-d01228ac9e50/go.mod h1:zPn1wHpTIePGnXSHpsVPWEktKXHr6+SS6x/IKRb7cpw= 109 | github.com/shurcooL/httperror v0.0.0-20170206035902-86b7830d14cc/go.mod h1:aYMfkZ6DWSJPJ6c4Wwz3QtW22G7mf/PEgaB9k/ik5+Y= 110 | github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= 111 | github.com/shurcooL/httpgzip v0.0.0-20180522190206-b1c53ac65af9/go.mod h1:919LwcH0M7/W4fcZ0/jy0qGght1GIhqyS/EgWGH2j5Q= 112 | github.com/shurcooL/issues v0.0.0-20181008053335-6292fdc1e191/go.mod h1:e2qWDig5bLteJ4fwvDAc2NHzqFEthkqn7aOZAOpj+PQ= 113 | github.com/shurcooL/issuesapp v0.0.0-20180602232740-048589ce2241/go.mod h1:NPpHK2TI7iSaM0buivtFUc9offApnI0Alt/K8hcHy0I= 114 | github.com/shurcooL/notifications v0.0.0-20181007000457-627ab5aea122/go.mod h1:b5uSkrEVM1jQUspwbixRBhaIjIzL2xazXp6kntxYle0= 115 | github.com/shurcooL/octicon v0.0.0-20181028054416-fa4f57f9efb2/go.mod h1:eWdoE5JD4R5UVWDucdOPg1g2fqQRq78IQa9zlOV1vpQ= 116 | github.com/shurcooL/reactions v0.0.0-20181006231557-f2e0b4ca5b82/go.mod h1:TCR1lToEk4d2s07G3XGfz2QrgHXg4RJBvjrOozvoWfk= 117 | github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= 118 | github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4= 119 | github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw= 120 | github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE= 121 | github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA= 122 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 123 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 124 | github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= 125 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 126 | github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= 127 | github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU= 128 | github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM= 129 | github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= 130 | go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= 131 | go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= 132 | go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= 133 | go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE= 134 | golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw= 135 | golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 136 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 137 | golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 138 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 139 | golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= 140 | golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= 141 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 142 | golang.org/x/exp v0.0.0-20221205204356-47842c84f3db h1:D/cFflL63o2KSLJIwjlcIt8PR064j/xsmdEJL/YvY/o= 143 | golang.org/x/exp v0.0.0-20221205204356-47842c84f3db/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= 144 | golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 145 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 146 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 147 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= 148 | golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= 149 | golang.org/x/mod v0.11.0 h1:bUO06HqtnRcc/7l71XBe4WcqTZ+3AH1J59zWDDwLKgU= 150 | golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= 151 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 152 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 153 | golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 154 | golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 155 | golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 156 | golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 157 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 158 | golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 159 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 160 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 161 | golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= 162 | golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= 163 | golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= 164 | golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= 165 | golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= 166 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 167 | golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 168 | golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 169 | golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 170 | golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw= 171 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 172 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 173 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 174 | golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 175 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 176 | golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 177 | golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 178 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 179 | golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 180 | golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 181 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 182 | golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 183 | golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 184 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 185 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 186 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 187 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 188 | golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 189 | golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 190 | golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= 191 | golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 192 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 193 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 194 | golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= 195 | golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= 196 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 197 | golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 198 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 199 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 200 | golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= 201 | golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= 202 | golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= 203 | golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= 204 | golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 205 | golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 206 | golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= 207 | golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= 208 | golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 209 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 210 | golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 211 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 212 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 213 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 214 | golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= 215 | golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= 216 | golang.org/x/tools v0.9.1 h1:8WMNJAz3zrtPmnYC7ISf5dEn3MT0gY7jBJfw27yrrLo= 217 | golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= 218 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 219 | google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= 220 | google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= 221 | google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y= 222 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 223 | google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 224 | google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 225 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 226 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 227 | google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 228 | google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 229 | google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg= 230 | google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 231 | google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= 232 | google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= 233 | google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= 234 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 235 | google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= 236 | google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= 237 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 238 | gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= 239 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 240 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 241 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 242 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 243 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 244 | grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o= 245 | honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 246 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 247 | honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 248 | sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck= 249 | sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0= 250 | -------------------------------------------------------------------------------- /iptest-txt/README.md: -------------------------------------------------------------------------------- 1 | ## 一键测速并上传到git仓库 2 | 3 | ### 先安装依赖库 4 | ```py 5 | pip install telethon pysocks 6 | ``` 7 | 8 | ### linux 系统 9 | ```bash 10 | python3 downip.py && bash formatip.sh && bash ipspeedtest.sh && bash uploadcsv.sh && echo "✅ 所有任务执行完成!" 11 | ``` 12 | 13 | ### win 系统 14 | 安装依赖 `pip install telethon pysocks` 15 | 先安装 gitbash 和 curl 16 | 再运行 run.bat 17 | 18 | ### IP 库 19 | ip库来自tg频道:https://t.me/Marisa_kristi 20 | 21 | ### 测速软件 22 | https://github.com/bh-qt/Cloudflare-IP-SpeedTest 23 | 测速结果 csv 文件全汉化 24 | -------------------------------------------------------------------------------- /iptest-txt/downip.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import asyncio 4 | import re 5 | from datetime import datetime, timedelta 6 | from collections import defaultdict 7 | from telethon import TelegramClient 8 | from telethon.tl.types import DocumentAttributeFilename 9 | 10 | # Windows 事件循环策略 11 | if sys.platform == 'win32': 12 | asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) 13 | 14 | # ================= 配置区域 ================= 15 | API_ID = '123456789' 16 | API_HASH = '532454gfghdgd87dtettwt6785444775' 17 | CHANNEL = 'Marisa_kristi' 18 | PROXY = ('socks5', '127.0.0.1', 10808, True) 19 | DOWNLOAD_DIR = 'ip' 20 | IP_MERGE = 'ip-443.txt' 21 | # ============================================ 22 | 23 | REGION_MAP = { 24 | '亚洲': 'as', '欧洲': 'eu', '美洲': 'am', 25 | '美国': 'us', '台湾': 'tw', '香港': 'hk', '新加坡': 'sg', '日本': 'jp', '韩国': 'kr' 26 | } 27 | 28 | # 定义颜色和符号 29 | COLORS = { 30 | 'red': '\033[91m', 'green': '\033[92m', 'yellow': '\033[93m', 31 | 'blue': '\033[94m', 'magenta': '\033[95m', 'cyan': '\033[96m', 32 | 'white': '\033[97m', 'reset': '\033[0m' 33 | } 34 | SYMBOLS = {'check': '✓', 'warning': '⚠', 'arrow': '➜', 'error': '✗'} 35 | 36 | # 日志函数 37 | def log(color, symbol, message): 38 | print(f"{COLORS[color]}{SYMBOLS[symbol]} {message}{COLORS['reset']}") 39 | 40 | # 主逻辑 41 | async def main(): 42 | os.makedirs(DOWNLOAD_DIR, exist_ok=True) # 初始化环境 43 | async with TelegramClient('tg_session', API_ID, API_HASH, proxy=PROXY) as client: 44 | channel = await client.get_entity(CHANNEL) # 获取频道消息 45 | log('cyan', 'arrow', f"已连接频道:{channel.title}") 46 | 47 | downloaded = await fetch_files(client, channel) # 下载文件 48 | await rename_files(downloaded) # 重命名文件 49 | await merge_all_files() # 合并ip库 50 | await clean_files() # 清理残留文件 51 | 52 | # 下载匹配 欧洲*ip*.txt、美洲*ip*.txt、亚洲*ip*.txt 的文件 53 | async def fetch_files(client, channel): 54 | downloaded = set() 55 | target_regions = ['欧洲', '美洲', '亚洲'] 56 | found_regions = set() 57 | # 遍历频道消息(最多300条) 58 | async for msg in client.iter_messages(channel, limit=300): 59 | if len(found_regions) == len(target_regions): 60 | break # 已找到所有目标文件 61 | 62 | if not (msg.document and (file_info := extract_file_info(msg))): 63 | continue 64 | 65 | fname, region = file_info 66 | if region in target_regions and region not in found_regions: 67 | beijing_time = msg.date + timedelta(hours=8) 68 | log('blue', 'arrow', f"消息时间: {beijing_time.strftime('%Y-%m-%d %H:%M:%S')} 文件名: {fname}") 69 | path = os.path.join(DOWNLOAD_DIR, fname) 70 | if not os.path.exists(path): 71 | await client.download_media(msg, path) 72 | log('green', 'check', f"下载完成:{fname}") 73 | downloaded.add(fname) 74 | found_regions.add(region) 75 | 76 | return downloaded 77 | 78 | # 提取文件名信息 79 | def extract_file_info(message_or_filename): 80 | if isinstance(message_or_filename, str): 81 | fname = message_or_filename 82 | else: 83 | attr = next((a for a in message_or_filename.document.attributes 84 | if isinstance(a, DocumentAttributeFilename)), None) 85 | if not attr: 86 | return None 87 | fname = attr.file_name 88 | 89 | # 文件匹配:包含地区名、包含ip、以.txt结尾 90 | for region in ['欧洲', '美洲', '亚洲']: 91 | if (region in fname and 92 | 'ip' in fname.lower() and 93 | fname.lower().endswith('.txt')): 94 | return (fname, region) 95 | return None 96 | 97 | # 重命名文件 98 | async def rename_files(downloaded): 99 | for fname in downloaded: 100 | file_info = extract_file_info(fname) 101 | if not file_info: 102 | continue 103 | 104 | fname, region = file_info 105 | target = f"ip-{REGION_MAP[region]}.txt" 106 | target_path = os.path.join(DOWNLOAD_DIR, target) 107 | 108 | if os.path.exists(target_path): 109 | os.remove(target_path) 110 | os.rename(os.path.join(DOWNLOAD_DIR, fname), target_path) 111 | log('green', 'check', f"已重命名:{fname} -> {target}") 112 | 113 | # 合并所有文件,但保留原始地区文件 114 | async def merge_all_files(): 115 | merge_path = os.path.join(DOWNLOAD_DIR, IP_MERGE) 116 | log('cyan', 'arrow', f"开始合并最终文件到 {IP_MERGE}...") 117 | 118 | try: 119 | with open(merge_path, 'w', encoding='utf-8') as merge_f: 120 | # 遍历目录中所有ip-开头的txt文件(排除合并文件自身) 121 | for fname in os.listdir(DOWNLOAD_DIR): 122 | if fname.endswith('.txt') and fname.startswith('ip-') and fname != IP_MERGE: 123 | file_path = os.path.join(DOWNLOAD_DIR, fname) 124 | with open(file_path, 'r', encoding='utf-8') as region_f: 125 | if content := region_f.read().strip(): 126 | merge_f.write(content + '\n') 127 | log('green', 'check', f"已合并:{fname}") 128 | 129 | log('cyan', 'arrow', "成功合并所有地区文件") 130 | 131 | except Exception as e: 132 | log('red', 'error', f"文件合并失败: {str(e)}") 133 | # 清理不完整文件 134 | if os.path.exists(merge_path): 135 | os.remove(merge_path) 136 | 137 | # 清理不符合命名规则的txt文件 138 | async def clean_files(): 139 | for fname in os.listdir(DOWNLOAD_DIR): 140 | if fname.endswith('.txt') and not fname.startswith('ip-'): 141 | try: 142 | os.remove(os.path.join(DOWNLOAD_DIR, fname)) 143 | log('yellow', 'warning', f"已清理:{fname}") 144 | except Exception as e: 145 | log('red', 'error', f"清理失败:{fname} - {str(e)}") 146 | 147 | if __name__ == '__main__': 148 | log('cyan', 'arrow', "开始下载ip库...") 149 | asyncio.run(main()) 150 | log('green', 'check', f"任务完成!文件保存在:{os.path.abspath(DOWNLOAD_DIR)}") 151 | -------------------------------------------------------------------------------- /iptest-txt/formatip.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 定义颜色和状态符号 4 | green="\033[1;32m" # 绿色(成功状态) 5 | blue="\033[1;34m" # 蓝色(信息提示) 6 | cyan="\033[1;36m" # 青色(进度信息) 7 | purple="\033[1;35m" # 紫色(分隔线) 8 | red="\033[1;31m" # 红色(错误状态) 9 | reset="\033[0m" # 重置颜色 10 | error="✗" # 错误符号 11 | tick="✔" # 成功符号 12 | arrow="➜" # 进度符号 13 | separator="====================================" # 分隔线 14 | 15 | # 日志函数 16 | log_error() { echo -e "${red}${error} $1${reset}"; } 17 | log_success() { echo -e "${green}${tick} $1${reset}"; } 18 | log_info() { echo -e "${cyan}${arrow} $1${reset}"; } 19 | 20 | # 导出日志函数,以便在子进程中调用 21 | export -f log_error log_success log_info 22 | 23 | # 定义输入和输出文件夹路径 24 | input_folder="ip" 25 | output_folder="ip2" 26 | origin_txt="ip-443.txt" 27 | merge_txt="ip-443-2.txt" 28 | 29 | # 检查输入文件夹是否存在且包含有效的文件 30 | if [ ! -d "$input_folder" ] || [ -z "$(ls -A "$input_folder"/ip-*.txt 2>/dev/null)" ]; then 31 | log_error "文件夹 $input_folder 不存在或为空,请检查输入文件" 32 | exit 1 33 | fi 34 | 35 | # 如果输出文件夹不存在,则创建 36 | if [ ! -d "$output_folder" ]; then 37 | mkdir -p "$output_folder" 38 | log_success "创建输出文件夹: $output_folder" 39 | fi 40 | 41 | # 定义格式化函数 42 | format_file() { 43 | local input_file="$1" output_file="$2" 44 | > "$output_file" # 清空或创建输出文件 45 | log_info "正在处理文件: $(basename "$input_file") -> $(basename "$output_file")" 46 | 47 | local total_lines=$(wc -l < "$input_file") processed_lines=0 48 | if [ "$total_lines" -eq 0 ]; then 49 | log_error "文件 $(basename "$input_file") 为空,跳过处理" 50 | return 51 | fi 52 | 53 | while IFS= read -r line; do 54 | line=$(echo "$line" | tr -d '\r\n') 55 | echo "$(cut -d ':' -f 1 <<< "$line") $(cut -d ':' -f 2 <<< "$line" | cut -d '#' -f 1)" >> "$output_file" 56 | processed_lines=$((processed_lines + 1)) 57 | local progress=$((processed_lines * 100 / total_lines)) 58 | # 绘制进度条 59 | local bar_length=20 60 | local filled_length=$((progress * bar_length / 100)) 61 | local bar=$(printf "%-${bar_length}s" | tr ' ' '=') 62 | printf "${cyan}${arrow} 文件 $(basename "$input_file") 处理进度: [${bar:0:filled_length}>${bar:filled_length}] ${progress}%%(${processed_lines}行/${total_lines}行)\r${reset}" 63 | done < "$input_file" 64 | 65 | log_success "文件 $(basename "$input_file") 格式化完成,已生成 $(basename "$output_file")" 66 | echo -e "${purple}${separator}${reset}" 67 | } 68 | 69 | # 导出格式化函数,以便在子进程中调用 70 | export -f format_file 71 | 72 | # 使用 xargs 并行处理文件 73 | find "$input_folder" -name "ip-*.txt" ! -name "$origin_txt" -print0 | xargs -0 -n 1 -P 16 bash -c ' 74 | input_file="$1" 75 | output_file="'"$output_folder"'/$(basename "${1%.txt}2.txt")" 76 | format_file "$input_file" "$output_file" 77 | ' _ 78 | 79 | # 合并并去重所有输出文件,生成 ip-443-2.txt 80 | merge_file="$output_folder/$merge_txt" 81 | log_info "正在合并并去重所有输出文件,生成: $(basename "$merge_file")" 82 | find "$output_folder" -name "ip-*.txt" ! -name "$merge_txt" -print0 | xargs -0 cat | sort -u > "$merge_file" 83 | 84 | # 检查合并文件是否成功生成 85 | if [ -f "$merge_file" ]; then 86 | log_success "文件 $(basename "$merge_file") 已成功生成,共 $(wc -l < "$merge_file") 行" 87 | else 88 | log_error "文件 $(basename "$merge_file") 生成失败" 89 | fi 90 | 91 | # 输出最终结果 92 | log_success "所有文件处理完成!" 93 | log_info "输出文件已保存到: $output_folder" 94 | log_info "分地区 IP 库:" 95 | find "$output_folder" -name "ip-*.txt" ! -name "$merge_txt" -print0 | xargs -0 -n 1 basename | while read -r file; do 96 | log_info "$output_folder/$file" 97 | done 98 | echo -e "${purple}${separator}${reset}" 99 | log_info "合并后的 IP 库: $output_folder/$merge_txt" -------------------------------------------------------------------------------- /iptest-txt/ipspeedtest.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 定义颜色和符号 4 | reset="\033[0m" 5 | red="\033[1;91m" 6 | green="\033[1;32m" 7 | yellow="\033[1;33m" 8 | cyan="\033[1;36m" 9 | blue="\033[1;34m" 10 | check="✓" 11 | error="✗" 12 | arrow="➜" 13 | separator="====================================" 14 | 15 | # 日志函数 16 | log_error() { echo -e "${red}${error} $1${reset}"; } 17 | log_success() { echo -e "${green}${check} $1${reset}"; } 18 | log_info() { echo -e "${cyan}${arrow} $1${reset}"; } 19 | 20 | # 测速软件下载地址 21 | iptest_url="https://pan.811520.xyz/cdn/iptest-windows-amd64.zip" 22 | 23 | # 定义文件结构 24 | SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" 25 | ip_dir="${SCRIPT_DIR}/ip2" 26 | tools_dir="${SCRIPT_DIR}/tools" 27 | iptest_file="$tools_dir/iptest.exe" 28 | speedcsv_dir="${SCRIPT_DIR}/csv" 29 | merged_csv="$speedcsv_dir/ip443.csv" 30 | 31 | mkdir -p "$tools_dir" "$speedcsv_dir" 32 | 33 | # 定义测速参数(保留) 34 | speedtest="3" 35 | speedlimit="5" 36 | delay="220" 37 | speedtesturl="spurl.api.030101.xyz/50mb" 38 | 39 | # 检查必要的环境 40 | check_environment() { 41 | log_info "正在检查环境..." 42 | if ! command -v curl &> /dev/null && ! command -v wget &> /dev/null; then 43 | log_error "错误:需要安装 curl 或 wget" 44 | exit 1 45 | fi 46 | log_success "环境检查完成" 47 | } 48 | 49 | # 封装下载函数 50 | download_file() { 51 | local url="$1" 52 | local output="$2" 53 | local filename="$3" 54 | log_info "下载地址: $url" 55 | log_info "保存到: $output/$filename" 56 | 57 | if command -v curl &>/dev/null; then 58 | curl -L -s "$url" -o "$output/$filename" || { log_error "curl 下载 $filename 失败,改用 wget 下载"; return 1; } 59 | elif command -v wget &>/dev/null; then 60 | wget -q "$url" -O "$output/$filename" || { log_error "wget 下载 $filename 失败"; return 1; } 61 | else 62 | log_error "$filename 下载失败,请检查下载地址或网络连接" 63 | return 1 64 | fi 65 | log_success "$filename 下载成功" 66 | } 67 | 68 | # 检查是否已有 iptest.exe,如果没有,则下载最新版 69 | download_iptest() { 70 | [ -f "$iptest_file" ] && { log_success "$iptest_file 文件已存在,跳过下载"; return 0; } 71 | log_error "未找到 $iptest_file, 尝试从 GitHub 下载..." 72 | 73 | [ ! -d "$tools_dir" ] && mkdir -p "$tools_dir" 74 | local iptestzip="iptest.zip" 75 | download_file "$iptest_url" "$tools_dir" "$iptestzip" || { log_error "文件不存在,下载失败,请手动下载并解压到 $tools_dir"; exit 1; } 76 | 77 | log_info "正在解压文件..." 78 | unzip -o "$tools_dir/$iptestzip" -d "$tools_dir" >/dev/null 2>&1 79 | if [ -f "$iptest_file" ]; then 80 | chmod +x "$iptest_file" || { log_error "设置执行权限失败"; return 1; } 81 | log_success "iptest 解压完成" 82 | else 83 | log_error "iptest 解压失败" 84 | return 1 85 | fi 86 | rm -f "$tools_dir/$iptestzip" 87 | } 88 | 89 | # 测速函数 90 | speed_test() { 91 | local input_file="$1" 92 | local output_file="$2" 93 | log_info "正在对文件 $input_file 进行测速..." 94 | "$iptest_file" -file="$input_file" -max=300 -speedtest="$speedtest" -delay="$delay" -speedlimit="$speedlimit" -url="$speedtesturl" -outfile="$output_file" 95 | [ $? -ne 0 ] && { log_error "测速失败: $input_file"; return 1; } 96 | log_success "测速完成: $input_file -> $output_file" 97 | } 98 | 99 | # 合并所有 CSV 文件 100 | merge_csv() { 101 | > "$merged_csv" # 清空目标文件 102 | local first_csv=$(ls "$speedcsv_dir"/ip-*2.csv 2>/dev/null | head -n 1) 103 | 104 | # 如果找到第一个 CSV 文件,将其标题行写入目标文件 105 | if [ -f "$first_csv" ]; then 106 | head -n 1 "$first_csv" >> "$merged_csv" 107 | fi 108 | 109 | # 遍历所有符合条件的 CSV 文件,# 排除 $merged_csv 文件,将其内容(跳过标题行)追加到目标文件 110 | for csv_file in "$speedcsv_dir"/ip-*2.csv; do 111 | if [ -f "$csv_file" ] && [ "$csv_file" != "$merged_csv" ]; then 112 | tail -n +2 "$csv_file" >> "$merged_csv" 113 | rm "$csv_file" # 删除已合并的 CSV 文件 114 | fi 115 | done 116 | 117 | log_success "所有 CSV 文件已合并为: $merged_csv" 118 | } 119 | 120 | # 主程序执行 121 | echo -e "${blue}${separator}${reset}" 122 | check_environment 123 | download_iptest 124 | 125 | # 检查 ip_dir 是否存在且包含有效的 IP 文件 126 | if [ ! -d "$ip_dir" ] || [ -z "$(ls -A "$ip_dir"/ip-*-2.txt 2>/dev/null)" ]; then 127 | log_error "文件夹 $ip_dir 不存在或为空,请检查输入文件" 128 | exit 1 129 | fi 130 | 131 | log_error "即将开始本地测速,请先关闭代理软件!" 132 | read -p "按回车键继续测速,按其他任意键退出..." -n 1 key 133 | echo 134 | [ "$key" != "" ] && { log_error "用户取消操作"; exit 0; } 135 | log_success "开始进行本地测速,请稍候..." 136 | 137 | for input_file in "$ip_dir"/ip-*-2.txt; do 138 | filename=$(basename "$input_file") 139 | csv_file="$speedcsv_dir/${filename%.txt}.csv" 140 | speed_test "$input_file" "$csv_file" 141 | done 142 | 143 | log_success "所有文件测速完成!" 144 | log_success "测速结果已保存到: $speedcsv_dir" 145 | merge_csv 146 | echo -e "${blue}${separator}${reset}" -------------------------------------------------------------------------------- /iptest-txt/run.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | chcp 65001 >nul 3 | 4 | rem 检查 Git 是否安装 5 | where git >nul 2>&1 6 | if %errorlevel% neq 0 ( 7 | echo 错误:未找到 Git,请先安装 Git。 8 | pause 9 | exit /b 1 10 | ) 11 | 12 | rem 检查 Python 是否安装 13 | where python >nul 2>&1 14 | if %errorlevel% neq 0 ( 15 | echo 错误:未找到 Python,请先安装 Python。 16 | pause 17 | exit /b 1 18 | ) 19 | 20 | rem 下载IP库 21 | echo 正在下载IP库... 22 | py "./downip.py" 23 | if %errorlevel% neq 0 ( 24 | echo 下载IP库失败,请检查以下内容: 25 | echo 1. 确保 downip.py 脚本存在且可执行。 26 | echo 2. 检查网络连接是否正常。 27 | echo 3. 确认 Telegram 频道的 API 配置是否正确。 28 | pause 29 | exit /b 1 30 | ) 31 | timeout /t 5 /nobreak >nul 32 | 33 | rem 格式化IP库 34 | echo 正在格式化IP库... 35 | "D:\Program Files\Git\bin\bash.exe" -c "./formatip.sh" 36 | if %errorlevel% neq 0 ( 37 | echo 格式化IP库失败,请检查以下内容: 38 | echo 1. 确保 formatip.sh 脚本存在且可执行。 39 | echo 2. 确认输入文件夹 ip 是否存在且包含有效的文件。 40 | pause 41 | exit /b 1 42 | ) 43 | timeout /t 5 /nobreak >nul 44 | 45 | rem 开始测速 46 | echo 正在测速... 47 | "D:/Program Files/Git/bin/bash.exe" -c "./ipspeedtest.sh" 48 | if %errorlevel% neq 0 ( 49 | echo 测速失败,请检查以下内容: 50 | echo 1. 确保 ipspeedtest.sh 脚本存在且可执行。 51 | echo 2. 检查网络连接是否正常。 52 | pause 53 | exit /b 1 54 | ) 55 | timeout /t 5 /nobreak >nul 56 | 57 | rem 提示用户确认是否执行上传脚本 58 | :confirm_prompt 59 | set /p confirm=是否将测速结果文件提交到 git (Y/N)? 60 | if "%confirm%" == "" ( 61 | echo 输入无效,请输入 Y 或 N。 62 | goto confirm_prompt 63 | ) 64 | set confirm=%confirm:~0,1% 65 | if /i "%confirm%" == "Y" ( 66 | echo 正在上传测速结果... 67 | "D:/Program Files/Git/bin/bash.exe" -c "./uploadcsv.sh" 68 | if %errorlevel% neq 0 ( 69 | echo 上传失败,请检查以下内容: 70 | echo 1. 确保 uploadcsv.sh 脚本存在且可执行。 71 | echo 2. 检查网络连接是否正常。 72 | echo 3. 确认 GitHub Token 和仓库配置是否正确。 73 | ) else ( 74 | echo 提交成功! 75 | ) 76 | ) else if /i "%confirm%" == "N" ( 77 | echo 已取消提交操作。 78 | ) else ( 79 | echo 输入无效,请输入 Y 或 N。 80 | goto confirm_prompt 81 | ) 82 | 83 | echo. 84 | echo 按任意键退出... 85 | pause >nul -------------------------------------------------------------------------------- /iptest-txt/tools/iptest.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yutian81/IP-SpeedTest/7069fe773c39988b6d8ad2b8453d5e811692802a/iptest-txt/tools/iptest.exe -------------------------------------------------------------------------------- /iptest-txt/uploadcsv.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 定义颜色和符号 4 | reset="\033[0m" 5 | red="\033[1;91m" 6 | green="\033[1;32m" 7 | yellow="\033[1;33m" 8 | cyan="\033[1;36m" 9 | blue="\033[1;34m" 10 | check="✓" 11 | error="✗" 12 | arrow="➜" 13 | separator="====================================" # 分隔线 14 | 15 | # 日志函数 16 | log_error() { echo -e "${red}${error} $1${reset}"; } 17 | log_success() { echo -e "${green}${check} $1${reset}"; } 18 | log_info() { echo -e "${cyan}${arrow} $1${reset}"; } 19 | 20 | # 定义 GitHub 参数 21 | GH_TOKEN="ghp_ggsgsg6778r6eetetyettrwg" 22 | GH_EMAIL="123abc@hotmail.com" 23 | GH_USER="yutianqq" 24 | GH_REPO="iptest" 25 | GH_BRANCH="main" 26 | 27 | # 定义文件路径 28 | SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" 29 | speedcsv_dir="${SCRIPT_DIR}/csv" 30 | 31 | # 检查 speedcsv_dir 是否存在且包含有效的 CSV 文件 32 | if [ ! -d "$speedcsv_dir" ] || [ -z "$(ls -A "$speedcsv_dir"/ip*.csv 2>/dev/null)" ]; then 33 | log_error "文件夹 $speedcsv_dir 或测速 csv 文件不存在或为空,请检查输入文件" 34 | exit 1 35 | fi 36 | 37 | # 验证必要参数 38 | if [ -z "$GH_TOKEN" ] || [ -z "$GH_USER" ] || [ -z "$GH_REPO" ] || [ -z "$GH_BRANCH" ]; then 39 | log_error "必要的 GitHub 参数未设置" 40 | exit 1 41 | fi 42 | 43 | # 上传测速结果到 GitHub 44 | upload_csv() { 45 | echo -e "${blue}${separator}${reset}" 46 | log_info "当前脚本所在的目录是: $SCRIPT_DIR" 47 | log_info "测速文件目录为: $speedcsv_dir" 48 | 49 | # 配置 Git 用户信息 50 | git config --global user.email "${GH_EMAIL}" 51 | git config --global user.name "${GH_USER}" 52 | 53 | # 创建临时目录 54 | tmp_dir=$(mktemp -d) 55 | trap 'rm -rf "$tmp_dir"' EXIT 56 | 57 | cd "$tmp_dir" || { 58 | log_error "无法进入临时目录" 59 | return 1 60 | } 61 | 62 | # 克隆指定的仓库 63 | clone_url="https://x-access-token:${GH_TOKEN}@github.com/${GH_USER}/${GH_REPO}.git" 64 | log_info "正在克隆仓库: https://github.com/${GH_USER}/${GH_REPO}" 65 | 66 | if ! git clone --depth 1 --branch "$GH_BRANCH" "$clone_url" 2>&1; then 67 | log_error "克隆失败,请检查仓库变量或网络连接" 68 | return 1 69 | fi 70 | 71 | # 进入仓库目录 72 | cd "$GH_REPO" || { 73 | log_error "无法进入仓库目录" 74 | return 1 75 | } 76 | 77 | # 复制所有测速文件 78 | for csv_file in "$speedcsv_dir"/ip*.csv; do 79 | filename=$(basename "$csv_file") 80 | log_info "正在上传文件: $filename" 81 | cp -f "$csv_file" . 82 | git add "$filename" 83 | done 84 | 85 | # 检查暂存区是否有变化并提交 86 | if git diff --cached --quiet; then 87 | log_success "没有更改可提交" 88 | else 89 | if ! git commit -m "更新测速文件 $(date +'%Y-%m-%d %H:%M:%S')"; then 90 | log_error "Git 提交失败" 91 | return 1 92 | fi 93 | 94 | # 推送到指定分支 95 | if ! git push origin "$GH_BRANCH"; then 96 | log_error "上传到 GitHub 仓库失败" 97 | return 1 98 | fi 99 | 100 | log_success "测速文件已推送" 101 | fi 102 | 103 | return 0 104 | } 105 | 106 | # 执行上传函数 107 | upload_csv 108 | 109 | # 检查执行结果 110 | if [ $? -ne 0 ]; then 111 | log_error "上传失败,请检查网络连接" 112 | exit 1 113 | else 114 | log_success "上传完成" 115 | exit 0 116 | fi -------------------------------------------------------------------------------- /iptest.go代码: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "bytes" 6 | "encoding/csv" 7 | "encoding/json" 8 | "flag" 9 | "fmt" 10 | "io" 11 | "io/ioutil" 12 | "net" 13 | "net/http" 14 | "os" 15 | "os/exec" 16 | "regexp" 17 | "runtime" 18 | "sort" 19 | "strconv" 20 | "strings" 21 | "sync" 22 | "time" 23 | ) 24 | 25 | const ( 26 | requestURL = "speed.cloudflare.com/cdn-cgi/trace" // 请求trace URL 27 | timeout = 1 * time.Second // 超时时间 28 | maxDuration = 2 * time.Second // 最大持续时间 29 | ) 30 | 31 | var ( 32 | File = flag.String("file", "ip.txt", "IP地址文件名称,格式为 ip port ,就是IP和端口之间用空格隔开") // IP地址文件名称 33 | outFile = flag.String("outfile", "ip.csv", "输出文件名称") // 输出文件名称 34 | maxThreads = flag.Int("max", 100, "并发请求最大协程数") // 最大协程数 35 | speedTest = flag.Int("speedtest", 5, "下载测速协程数量,设为0禁用测速") // 下载测速协程数量 36 | speedTestURL = flag.String("url", "speed.cloudflare.com/__down?bytes=500000000", "测速文件地址") // 测速文件地址 37 | enableTLS = flag.Bool("tls", true, "是否启用TLS") // TLS是否启用 38 | ) 39 | 40 | type result struct { 41 | ip string // IP地址 42 | port int // 端口 43 | dataCenter string // 数据中心 44 | region string // 地区 45 | city string // 城市 46 | latency string // 延迟 47 | tcpDuration time.Duration // TCP请求延迟 48 | } 49 | 50 | type speedtestresult struct { 51 | result 52 | downloadSpeed float64 // 下载速度 53 | } 54 | 55 | type location struct { 56 | Iata string `json:"iata"` 57 | Lat float64 `json:"lat"` 58 | Lon float64 `json:"lon"` 59 | Cca2 string `json:"cca2"` 60 | Region string `json:"region"` 61 | City string `json:"city"` 62 | } 63 | 64 | // 尝试提升文件描述符的上限 65 | func increaseMaxOpenFiles() { 66 | fmt.Println("正在尝试提升文件描述符的上限...") 67 | cmd := exec.Command("bash", "-c", "ulimit -n 10000") 68 | _, err := cmd.CombinedOutput() 69 | if err != nil { 70 | fmt.Printf("提升文件描述符上限时出现错误: %v\n", err) 71 | } else { 72 | fmt.Printf("文件描述符上限已提升!\n") 73 | } 74 | } 75 | 76 | func main() { 77 | flag.Parse() 78 | 79 | startTime := time.Now() 80 | osType := runtime.GOOS 81 | if osType == "linux" { 82 | increaseMaxOpenFiles() 83 | } 84 | 85 | var locations []location 86 | if _, err := os.Stat("locations.json"); os.IsNotExist(err) { 87 | fmt.Println("本地 locations.json 不存在\n正在从 https://speed.cloudflare.com/locations 下载 locations.json") 88 | resp, err := http.Get("https://speed.cloudflare.com/locations") 89 | if err != nil { 90 | fmt.Printf("无法从URL中获取JSON: %v\n", err) 91 | return 92 | } 93 | 94 | defer resp.Body.Close() 95 | 96 | body, err := ioutil.ReadAll(resp.Body) 97 | if err != nil { 98 | fmt.Printf("无法读取响应体: %v\n", err) 99 | return 100 | } 101 | 102 | err = json.Unmarshal(body, &locations) 103 | if err != nil { 104 | fmt.Printf("无法解析JSON: %v\n", err) 105 | return 106 | } 107 | file, err := os.Create("locations.json") 108 | if err != nil { 109 | fmt.Printf("无法创建文件: %v\n", err) 110 | return 111 | } 112 | defer file.Close() 113 | 114 | _, err = file.Write(body) 115 | if err != nil { 116 | fmt.Printf("无法写入文件: %v\n", err) 117 | return 118 | } 119 | } else { 120 | fmt.Println("本地 locations.json 已存在,无需重新下载") 121 | file, err := os.Open("locations.json") 122 | if err != nil { 123 | fmt.Printf("无法打开文件: %v\n", err) 124 | return 125 | } 126 | defer file.Close() 127 | 128 | body, err := ioutil.ReadAll(file) 129 | if err != nil { 130 | fmt.Printf("无法读取文件: %v\n", err) 131 | return 132 | } 133 | 134 | err = json.Unmarshal(body, &locations) 135 | if err != nil { 136 | fmt.Printf("无法解析JSON: %v\n", err) 137 | return 138 | } 139 | } 140 | 141 | locationMap := make(map[string]location) 142 | for _, loc := range locations { 143 | locationMap[loc.Iata] = loc 144 | } 145 | 146 | ips, err := readIPs(*File) 147 | if err != nil { 148 | fmt.Printf("无法从文件中读取 IP: %v\n", err) 149 | return 150 | } 151 | 152 | var wg sync.WaitGroup 153 | wg.Add(len(ips)) 154 | 155 | resultChan := make(chan result, len(ips)) 156 | 157 | thread := make(chan struct{}, *maxThreads) 158 | 159 | var count int 160 | total := len(ips) 161 | 162 | for _, ip := range ips { 163 | thread <- struct{}{} 164 | go func(ip string) { 165 | defer func() { 166 | <-thread 167 | wg.Done() 168 | count++ 169 | percentage := float64(count) / float64(total) * 100 170 | fmt.Printf("已完成: %d 总数: %d 已完成: %.2f%%\r", count, total, percentage) 171 | if count == total { 172 | fmt.Printf("已完成: %d 总数: %d 已完成: %.2f%%\n", count, total, percentage) 173 | } 174 | }() 175 | 176 | parts := strings.Fields(ip) 177 | if len(parts) != 2 { 178 | fmt.Printf("IP地址格式错误: %s\n", ip) 179 | return 180 | } 181 | ipAddr := parts[0] 182 | portStr := parts[1] 183 | 184 | port, err := strconv.Atoi(portStr) 185 | if err != nil { 186 | fmt.Printf("端口格式错误: %s\n", portStr) 187 | return 188 | } 189 | 190 | dialer := &net.Dialer{ 191 | Timeout: timeout, 192 | KeepAlive: 0, 193 | } 194 | start := time.Now() 195 | conn, err := dialer.Dial("tcp", net.JoinHostPort(ipAddr, strconv.Itoa(port))) 196 | if err != nil { 197 | return 198 | } 199 | defer conn.Close() 200 | 201 | tcpDuration := time.Since(start) 202 | start = time.Now() 203 | 204 | client := http.Client{ 205 | Transport: &http.Transport{ 206 | Dial: func(network, addr string) (net.Conn, error) { 207 | return conn, nil 208 | }, 209 | }, 210 | Timeout: timeout, 211 | } 212 | 213 | var protocol string 214 | if *enableTLS { 215 | protocol = "https://" 216 | } else { 217 | protocol = "http://" 218 | } 219 | requestURL := protocol + requestURL 220 | 221 | req, _ := http.NewRequest("GET", requestURL, nil) 222 | 223 | // 添加用户代理 224 | req.Header.Set("User-Agent", "Mozilla/5.0") 225 | req.Close = true 226 | resp, err := client.Do(req) 227 | if err != nil { 228 | return 229 | } 230 | 231 | duration := time.Since(start) 232 | if duration > maxDuration { 233 | return 234 | } 235 | 236 | defer resp.Body.Close() 237 | buf := &bytes.Buffer{} 238 | // 创建一个读取操作的超时 239 | timeout := time.After(maxDuration) 240 | // 使用一个 goroutine 来读取响应体 241 | done := make(chan bool) 242 | go func() { 243 | _, err := io.Copy(buf, resp.Body) 244 | done <- true 245 | if err != nil { 246 | return 247 | } 248 | }() 249 | // 等待读取操作完成或者超时 250 | select { 251 | case <-done: 252 | // 读取操作完成 253 | case <-timeout: 254 | // 读取操作超时 255 | return 256 | } 257 | 258 | body := buf 259 | if err != nil { 260 | return 261 | } 262 | 263 | if strings.Contains(body.String(), "uag=Mozilla/5.0") { 264 | if matches := regexp.MustCompile(`colo=([A-Z]+)`).FindStringSubmatch(body.String()); len(matches) > 1 { 265 | dataCenter := matches[1] 266 | loc, ok := locationMap[dataCenter] 267 | if ok { 268 | fmt.Printf("发现有效IP %s 端口 %d 位置信息 %s 延迟 %d 毫秒\n", ipAddr, port, loc.City, tcpDuration.Milliseconds()) 269 | resultChan <- result{ipAddr, port, dataCenter, loc.Region, loc.City, fmt.Sprintf("%d ms", tcpDuration.Milliseconds()), tcpDuration} 270 | } else { 271 | fmt.Printf("发现有效IP %s 端口 %d 位置信息未知 延迟 %d 毫秒\n", ipAddr, port, tcpDuration.Milliseconds()) 272 | resultChan <- result{ipAddr, port, dataCenter, "", "", fmt.Sprintf("%d ms", tcpDuration.Milliseconds()), tcpDuration} 273 | } 274 | } 275 | } 276 | }(ip) 277 | } 278 | 279 | wg.Wait() 280 | close(resultChan) 281 | 282 | if len(resultChan) == 0 { 283 | // 清除输出内容 284 | fmt.Print("\033[2J") 285 | fmt.Println("没有发现有效的IP") 286 | return 287 | } 288 | var results []speedtestresult 289 | if *speedTest > 0 { 290 | fmt.Printf("开始测速\n") 291 | var wg2 sync.WaitGroup 292 | wg2.Add(*speedTest) 293 | count = 0 294 | total := len(resultChan) 295 | results = []speedtestresult{} 296 | for i := 0; i < *speedTest; i++ { 297 | thread <- struct{}{} 298 | go func() { 299 | defer func() { 300 | <-thread 301 | wg2.Done() 302 | }() 303 | for res := range resultChan { 304 | 305 | downloadSpeed := getDownloadSpeed(res.ip, res.port) 306 | results = append(results, speedtestresult{result: res, downloadSpeed: downloadSpeed}) 307 | 308 | count++ 309 | percentage := float64(count) / float64(total) * 100 310 | fmt.Printf("已完成: %.2f%%\r", percentage) 311 | if count == total { 312 | fmt.Printf("已完成: %.2f%%\033[0\n", percentage) 313 | } 314 | } 315 | }() 316 | } 317 | wg2.Wait() 318 | } else { 319 | for res := range resultChan { 320 | results = append(results, speedtestresult{result: res}) 321 | } 322 | } 323 | 324 | if *speedTest > 0 { 325 | sort.Slice(results, func(i, j int) bool { 326 | return results[i].downloadSpeed > results[j].downloadSpeed 327 | }) 328 | } else { 329 | sort.Slice(results, func(i, j int) bool { 330 | return results[i].result.tcpDuration < results[j].result.tcpDuration 331 | }) 332 | } 333 | 334 | file, err := os.Create(*outFile) 335 | if err != nil { 336 | fmt.Printf("无法创建文件: %v\n", err) 337 | return 338 | } 339 | defer file.Close() 340 | 341 | writer := csv.NewWriter(file) 342 | if *speedTest > 0 { 343 | writer.Write([]string{"IP地址", "端口", "TLS", "数据中心", "地区", "城市", "网络延迟", "下载速度"}) 344 | } else { 345 | writer.Write([]string{"IP地址", "端口", "TLS", "数据中心", "地区", "城市", "网络延迟"}) 346 | } 347 | for _, res := range results { 348 | if *speedTest > 0 { 349 | writer.Write([]string{res.result.ip, strconv.Itoa(res.result.port), strconv.FormatBool(*enableTLS), res.result.dataCenter, res.result.region, res.result.city, res.result.latency, fmt.Sprintf("%.0f kB/s", res.downloadSpeed)}) 350 | } else { 351 | writer.Write([]string{res.result.ip, strconv.Itoa(res.result.port), strconv.FormatBool(*enableTLS), res.result.dataCenter, res.result.region, res.result.city, res.result.latency}) 352 | } 353 | } 354 | 355 | writer.Flush() 356 | // 清除输出内容 357 | fmt.Print("\033[2J") 358 | fmt.Printf("成功将结果写入文件 %s,耗时 %d秒\n", *outFile, time.Since(startTime)/time.Second) 359 | } 360 | 361 | // 从文件中读取IP地址和端口 362 | func readIPs(File string) ([]string, error) { 363 | file, err := os.Open(File) 364 | if err != nil { 365 | return nil, err 366 | } 367 | defer file.Close() 368 | var ips []string 369 | scanner := bufio.NewScanner(file) 370 | for scanner.Scan() { 371 | line := scanner.Text() 372 | parts := strings.Fields(line) 373 | if len(parts) != 2 { 374 | fmt.Printf("行格式错误: %s\n", line) 375 | continue 376 | } 377 | ipAddr := parts[0] 378 | portStr := parts[1] 379 | 380 | port, err := strconv.Atoi(portStr) 381 | if err != nil { 382 | fmt.Printf("端口格式错误: %s\n", portStr) 383 | continue 384 | } 385 | 386 | ip := fmt.Sprintf("%s %d", ipAddr, port) 387 | ips = append(ips, ip) 388 | } 389 | return ips, scanner.Err() 390 | } 391 | 392 | // inc函数实现ip地址自增 393 | func inc(ip net.IP) { 394 | for j := len(ip) - 1; j >= 0; j-- { 395 | ip[j]++ 396 | if ip[j] > 0 { 397 | break 398 | } 399 | } 400 | } 401 | 402 | // 测速函数 403 | func getDownloadSpeed(ip string, port int) float64 { 404 | var protocol string 405 | if *enableTLS { 406 | protocol = "https://" 407 | } else { 408 | protocol = "http://" 409 | } 410 | speedTestURL := protocol + *speedTestURL 411 | // 创建请求 412 | req, _ := http.NewRequest("GET", speedTestURL, nil) 413 | req.Header.Set("User-Agent", "Mozilla/5.0") 414 | 415 | // 创建TCP连接 416 | dialer := &net.Dialer{ 417 | Timeout: timeout, 418 | KeepAlive: 0, 419 | } 420 | conn, err := dialer.Dial("tcp", net.JoinHostPort(ip, strconv.Itoa(port))) 421 | if err != nil { 422 | return 0 423 | } 424 | defer conn.Close() 425 | 426 | fmt.Printf("正在测试IP %s 端口 %d\n", ip, port) 427 | startTime := time.Now() 428 | // 创建HTTP客户端 429 | client := http.Client{ 430 | Transport: &http.Transport{ 431 | Dial: func(network, addr string) (net.Conn, error) { 432 | return conn, nil 433 | }, 434 | }, 435 | //设置单个IP测速最长时间为5秒 436 | Timeout: 5 * time.Second, 437 | } 438 | // 发送请求 439 | req.Close = true 440 | resp, err := client.Do(req) 441 | if err != nil { 442 | fmt.Printf("IP %s 端口 %d 测速无效\n", ip, port) 443 | return 0 444 | } 445 | defer resp.Body.Close() 446 | 447 | // 复制响应体到/dev/null,并计算下载速度 448 | written, _ := io.Copy(io.Discard, resp.Body) 449 | duration := time.Since(startTime) 450 | speed := float64(written) / duration.Seconds() / 1024 451 | 452 | // 输出结果 453 | fmt.Printf("IP %s 端口 %d 下载速度 %.0f kB/s\n", ip, port, speed) 454 | return speed 455 | } 456 | -------------------------------------------------------------------------------- /locations.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "iata": "TIA", 4 | "lat": 41.4146995544, 5 | "lon": 19.7206001282, 6 | "country": "阿尔巴尼亚", 7 | "cca2": "AL", 8 | "region": "欧洲", 9 | "city": "地拉那" 10 | }, 11 | { 12 | "iata": "KHN", 13 | "lat": 41.4146995544, 14 | "lon": 19.7206001282, 15 | "country": "中国", 16 | "cca2": "CN", 17 | "region": "亚洲", 18 | "city": "南昌" 19 | }, 20 | { 21 | "iata": "ALG", 22 | "lat": 36.6910018921, 23 | "lon": 3.2154099941, 24 | "country": "阿尔及利亚", 25 | "cca2": "DZ", 26 | "region": "非洲", 27 | "city": "阿尔及尔" 28 | }, 29 | { 30 | "iata": "ORN", 31 | "lat": 35.6911, 32 | "lon": -0.6416, 33 | "country": "阿尔及利亚", 34 | "cca2": "DZ", 35 | "region": "非洲", 36 | "city": "奥兰" 37 | }, 38 | { 39 | "iata": "LAD", 40 | "lat": -8.8583698273, 41 | "lon": 13.2312002182, 42 | "country": "安哥拉", 43 | "cca2": "AO", 44 | "region": "非洲", 45 | "city": "罗安达" 46 | }, 47 | { 48 | "iata": "EZE", 49 | "lat": -34.8222, 50 | "lon": -58.5358, 51 | "country": "阿根廷", 52 | "cca2": "AR", 53 | "region": "南美", 54 | "city": "布宜诺斯艾利斯" 55 | }, 56 | { 57 | "iata": "COR", 58 | "lat": -31.31, 59 | "lon": -64.208333, 60 | "country": "阿根廷", 61 | "cca2": "AR", 62 | "region": "南美", 63 | "city": "科尔多瓦" 64 | }, 65 | { 66 | "iata": "NQN", 67 | "lat": -38.9490013123, 68 | "lon": -68.1557006836, 69 | "country": "阿根廷", 70 | "cca2": "AR", 71 | "region": "南美", 72 | "city": "内乌肯" 73 | }, 74 | { 75 | "iata": "EVN", 76 | "lat": 40.1473007202, 77 | "lon": 44.3959007263, 78 | "country": "亚美尼亚", 79 | "cca2": "AM", 80 | "region": "中东", 81 | "city": "埃里温" 82 | }, 83 | { 84 | "iata": "ADL", 85 | "lat": -34.9431729, 86 | "lon": 138.5335637, 87 | "country": "澳大利亚", 88 | "cca2": "AU", 89 | "region": "大洋洲", 90 | "city": "阿德莱德" 91 | }, 92 | { 93 | "iata": "BNE", 94 | "lat": -27.3841991425, 95 | "lon": 153.117004394, 96 | "country": "澳大利亚", 97 | "cca2": "AU", 98 | "region": "大洋洲", 99 | "city": "布里斯班" 100 | }, 101 | { 102 | "iata": "CBR", 103 | "lat": -35.3069000244, 104 | "lon": 149.1950073242, 105 | "country": "澳大利亚", 106 | "cca2": "AU", 107 | "region": "大洋洲", 108 | "city": "堪培拉" 109 | }, 110 | { 111 | "iata": "HBA", 112 | "lat": -42.883209, 113 | "lon": 147.331665, 114 | "country": "澳大利亚", 115 | "cca2": "AU", 116 | "region": "大洋洲", 117 | "city": "霍巴特" 118 | }, 119 | { 120 | "iata": "MEL", 121 | "lat": -37.6733016968, 122 | "lon": 144.843002319, 123 | "country": "澳大利亚", 124 | "cca2": "AU", 125 | "region": "大洋洲", 126 | "city": "墨尔本" 127 | }, 128 | { 129 | "iata": "PER", 130 | "lat": -31.9402999878, 131 | "lon": 115.967002869, 132 | "country": "澳大利亚", 133 | "cca2": "AU", 134 | "region": "大洋洲", 135 | "city": "珀斯" 136 | }, 137 | { 138 | "iata": "SYD", 139 | "lat": -33.9460983276, 140 | "lon": 151.177001953, 141 | "country": "澳大利亚", 142 | "cca2": "AU", 143 | "region": "大洋洲", 144 | "city": "悉尼" 145 | }, 146 | { 147 | "iata": "VIE", 148 | "lat": 48.1102981567, 149 | "lon": 16.5697002411, 150 | "country": "奥地利", 151 | "cca2": "AT", 152 | "region": "欧洲", 153 | "city": "维也纳" 154 | }, 155 | { 156 | "iata": "LLK", 157 | "lat": 38.7463989258, 158 | "lon": 48.8180007935, 159 | "country": "阿塞拜疆", 160 | "cca2": "AZ", 161 | "region": "中东", 162 | "city": "Astara" 163 | }, 164 | { 165 | "iata": "GYD", 166 | "lat": 40.4674987793, 167 | "lon": 50.0466995239, 168 | "country": "阿塞拜疆", 169 | "cca2": "AZ", 170 | "region": "中东", 171 | "city": "巴库" 172 | }, 173 | { 174 | "iata": "BAH", 175 | "lat": 26.2707996368, 176 | "lon": 50.6335983276, 177 | "country": "巴林", 178 | "cca2": "BH", 179 | "region": "中东", 180 | "city": "麦纳麦" 181 | }, 182 | { 183 | "iata": "CGP", 184 | "lat": 22.2495995, 185 | "lon": 91.8133011, 186 | "country": "孟加拉国", 187 | "cca2": "BD", 188 | "region": "亚太", 189 | "city": "吉大港" 190 | }, 191 | { 192 | "iata": "DAC", 193 | "lat": 23.843347, 194 | "lon": 90.397783, 195 | "country": "孟加拉国", 196 | "cca2": "BD", 197 | "region": "亚太", 198 | "city": "达卡" 199 | }, 200 | { 201 | "iata": "JSR", 202 | "lat": 23.1837997437, 203 | "lon": 89.1607971191, 204 | "country": "孟加拉国", 205 | "cca2": "BD", 206 | "region": "亚太", 207 | "city": "杰索尔" 208 | }, 209 | { 210 | "iata": "MSQ", 211 | "lat": 53.9006, 212 | "lon": 27.599, 213 | "country": "白俄罗斯", 214 | "cca2": "BY", 215 | "region": "欧洲", 216 | "city": "明斯克" 217 | }, 218 | { 219 | "iata": "BRU", 220 | "lat": 50.9014015198, 221 | "lon": 4.4844398499, 222 | "country": "比利时", 223 | "cca2": "BE", 224 | "region": "欧洲", 225 | "city": "布鲁塞尔" 226 | }, 227 | { 228 | "iata": "PBH", 229 | "lat": 27.4712, 230 | "lon": 89.6339, 231 | "country": "不丹", 232 | "cca2": "BT", 233 | "region": "亚太", 234 | "city": "廷布" 235 | }, 236 | { 237 | "iata": "GBE", 238 | "lat": -24.6282, 239 | "lon": 25.9231, 240 | "country": "不丹", 241 | "cca2": "BW", 242 | "region": "非洲", 243 | "city": "博茨瓦纳" 244 | }, 245 | { 246 | "iata": "QWJ", 247 | "lat": -22.738, 248 | "lon": -47.334, 249 | "country": "巴西", 250 | "cca2": "BR", 251 | "region": "南美", 252 | "city": "亚美利加纳" 253 | }, 254 | { 255 | "iata": "BEL", 256 | "lat": -1.4563, 257 | "lon": -48.5013, 258 | "country": "巴西", 259 | "cca2": "BR", 260 | "region": "南美", 261 | "city": "贝伦" 262 | }, 263 | { 264 | "iata": "CNF", 265 | "lat": -19.624444, 266 | "lon": -43.971944, 267 | "country": "巴西", 268 | "cca2": "BR", 269 | "region": "南美", 270 | "city": "贝洛奥里宗特" 271 | }, 272 | { 273 | "iata": "BNU", 274 | "lat": -26.89245, 275 | "lon": -49.07696, 276 | "country": "巴西", 277 | "cca2": "BR", 278 | "region": "南美", 279 | "city": "布鲁梅瑙" 280 | }, 281 | { 282 | "iata": "BSB", 283 | "lat": -15.79824, 284 | "lon": -47.90859, 285 | "country": "巴西", 286 | "cca2": "BR", 287 | "region": "南美", 288 | "city": "利亚" 289 | }, 290 | { 291 | "iata": "CFC", 292 | "lat": -26.7762, 293 | "lon": -51.0125, 294 | "country": "巴西", 295 | "cca2": "BR", 296 | "region": "南美", 297 | "city": "卡萨多尔" 298 | }, 299 | { 300 | "iata": "VCP", 301 | "lat": -22.90662, 302 | "lon": -47.08576, 303 | "country": "巴西", 304 | "cca2": "BR", 305 | "region": "南美", 306 | "city": "坎皮纳斯" 307 | }, 308 | { 309 | "iata": "CAW", 310 | "lat": -21.698299408, 311 | "lon": -41.301700592, 312 | "country": "巴西", 313 | "cca2": "BR", 314 | "region": "南美", 315 | "city": "坎普斯" 316 | }, 317 | { 318 | "iata": "CGB", 319 | "lat": -15.59611, 320 | "lon": -56.09667, 321 | "country": "巴西", 322 | "cca2": "BR", 323 | "region": "南美", 324 | "city": "库亚巴" 325 | }, 326 | { 327 | "iata": "CWB", 328 | "lat": -25.5284996033, 329 | "lon": -49.1758003235, 330 | "country": "巴西", 331 | "cca2": "BR", 332 | "region": "南美", 333 | "city": "库里蒂巴" 334 | }, 335 | { 336 | "iata": "FLN", 337 | "lat": -27.6702785492, 338 | "lon": -48.5525016785, 339 | "country": "巴西", 340 | "cca2": "BR", 341 | "region": "南美", 342 | "city": "弗洛里亚诺波利斯" 343 | }, 344 | { 345 | "iata": "FOR", 346 | "lat": -3.7762799263, 347 | "lon": -38.5326004028, 348 | "country": "巴西", 349 | "cca2": "BR", 350 | "region": "南美", 351 | "city": "福塔莱萨" 352 | }, 353 | { 354 | "iata": "GYN", 355 | "lat": -16.69727, 356 | "lon": -49.26851, 357 | "country": "巴西", 358 | "cca2": "BR", 359 | "region": "南美", 360 | "city": "戈亚尼亚" 361 | }, 362 | { 363 | "iata": "ITJ", 364 | "lat": -27.6116676331, 365 | "lon": -48.6727790833, 366 | "country": "巴西", 367 | "cca2": "BR", 368 | "region": "南美", 369 | "city": "伊塔雅伊" 370 | }, 371 | { 372 | "iata": "JOI", 373 | "lat": -26.304408, 374 | "lon": -48.846383, 375 | "country": "巴西", 376 | "cca2": "BR", 377 | "region": "南美", 378 | "city": "若茵维莱" 379 | }, 380 | { 381 | "iata": "JDO", 382 | "lat": -7.2242, 383 | "lon": -39.313, 384 | "country": "巴西", 385 | "cca2": "BR", 386 | "region": "南美", 387 | "city": "北茹阿泽鲁" 388 | }, 389 | { 390 | "iata": "MAO", 391 | "lat": -3.11286, 392 | "lon": -60.01949, 393 | "country": "巴西", 394 | "cca2": "BR", 395 | "region": "南美", 396 | "city": "马瑙斯" 397 | }, 398 | { 399 | "iata": "POA", 400 | "lat": -29.9944000244, 401 | "lon": -51.1713981628, 402 | "country": "巴西", 403 | "cca2": "BR", 404 | "region": "南美", 405 | "city": "阿雷格里港" 406 | }, 407 | { 408 | "iata": "REC", 409 | "lat": -8.1264896393, 410 | "lon": -34.9235992432, 411 | "country": "巴西", 412 | "cca2": "BR", 413 | "region": "南美", 414 | "city": "累西腓" 415 | }, 416 | { 417 | "iata": "RAO", 418 | "lat": -21.1363887787, 419 | "lon": -47.7766685486, 420 | "country": "巴西", 421 | "cca2": "BR", 422 | "region": "南美", 423 | "city": "里贝朗普雷图" 424 | }, 425 | { 426 | "iata": "GIG", 427 | "lat": -22.8099994659, 428 | "lon": -43.2505569458, 429 | "country": "巴西", 430 | "cca2": "BR", 431 | "region": "南美", 432 | "city": "里约热内卢" 433 | }, 434 | { 435 | "iata": "SSA", 436 | "lat": -12.9086112976, 437 | "lon": -38.3224983215, 438 | "country": "巴西", 439 | "cca2": "BR", 440 | "region": "南美", 441 | "city": "萨尔瓦多" 442 | }, 443 | { 444 | "iata": "SJP", 445 | "lat": -20.807157, 446 | "lon": -49.378994, 447 | "country": "巴西", 448 | "cca2": "BR", 449 | "region": "南美", 450 | "city": "圣若泽" 451 | }, 452 | { 453 | "iata": "SJK", 454 | "lat": -23.1791, 455 | "lon": -45.8872, 456 | "country": "巴西", 457 | "cca2": "BR", 458 | "region": "南美", 459 | "city": "圣若泽杜斯坎普斯" 460 | }, 461 | { 462 | "iata": "GRU", 463 | "lat": -23.4355564117, 464 | "lon": -46.4730567932, 465 | "country": "巴西", 466 | "cca2": "BR", 467 | "region": "南美", 468 | "city": "圣保罗" 469 | }, 470 | { 471 | "iata": "SOD", 472 | "lat": -23.54389, 473 | "lon": -46.63445, 474 | "country": "巴西", 475 | "cca2": "BR", 476 | "region": "南美", 477 | "city": "索罗卡巴" 478 | }, 479 | { 480 | "iata": "NVT", 481 | "lat": -26.8251, 482 | "lon": -49.2695, 483 | "country": "巴西", 484 | "cca2": "BR", 485 | "region": "南美", 486 | "city": "圣卡塔琳娜" 487 | }, 488 | { 489 | "iata": "UDI", 490 | "lat": -18.8836116791, 491 | "lon": -48.225276947, 492 | "country": "巴西", 493 | "cca2": "BR", 494 | "region": "南美", 495 | "city": "乌贝兰迪亚" 496 | }, 497 | { 498 | "iata": "VIX", 499 | "lat": -20.64871, 500 | "lon": -41.90857, 501 | "country": "巴西", 502 | "cca2": "BR", 503 | "region": "南美", 504 | "city": "维多利亚" 505 | }, 506 | { 507 | "iata": "BWN", 508 | "lat": 4.903052, 509 | "lon": 114.939819, 510 | "country": "文莱", 511 | "cca2": "BN", 512 | "region": "亚太", 513 | "city": "斯里巴加湾" 514 | }, 515 | { 516 | "iata": "SOF", 517 | "lat": 42.6966934204, 518 | "lon": 23.4114360809, 519 | "country": "保加利亚", 520 | "cca2": "BG", 521 | "region": "欧洲", 522 | "city": "索菲亚" 523 | }, 524 | { 525 | "iata": "OUA", 526 | "lat": 12.3531999588, 527 | "lon": -1.5124200583, 528 | "country": "布基纳法索", 529 | "cca2": "BF", 530 | "region": "非洲", 531 | "city": "瓦加杜古" 532 | }, 533 | { 534 | "iata": "PNH", 535 | "lat": 11.5466003418, 536 | "lon": 104.84400177, 537 | "country": "柬埔寨", 538 | "cca2": "KH", 539 | "region": "亚太", 540 | "city": "金边" 541 | }, 542 | { 543 | "iata": "YYC", 544 | "lat": 51.113899231, 545 | "lon": -114.019996643, 546 | "country": "加拿大", 547 | "cca2": "CA", 548 | "region": "北美洲", 549 | "city": "卡尔加里" 550 | }, 551 | { 552 | "iata": "YVR", 553 | "lat": 49.193901062, 554 | "lon": -123.183998108, 555 | "country": "加拿大", 556 | "cca2": "CA", 557 | "region": "北美洲", 558 | "city": "温哥华" 559 | }, 560 | { 561 | "iata": "YWG", 562 | "lat": 49.9099998474, 563 | "lon": -97.2398986816, 564 | "country": "加拿大", 565 | "cca2": "CA", 566 | "region": "北美洲", 567 | "city": "温尼伯" 568 | }, 569 | { 570 | "iata": "YOW", 571 | "lat": 45.3224983215, 572 | "lon": -75.6691970825, 573 | "country": "加拿大", 574 | "cca2": "CA", 575 | "region": "北美洲", 576 | "city": "渥太华" 577 | }, 578 | { 579 | "iata": "YYZ", 580 | "lat": 43.6772003174, 581 | "lon": -79.6305999756, 582 | "country": "加拿大", 583 | "cca2": "CA", 584 | "region": "北美洲", 585 | "city": "多伦多" 586 | }, 587 | { 588 | "iata": "YUL", 589 | "lat": 45.4706001282, 590 | "lon": -73.7407989502, 591 | "country": "加拿大", 592 | "cca2": "CA", 593 | "region": "北美洲", 594 | "city": "蒙特利尔" 595 | }, 596 | { 597 | "iata": "YXE", 598 | "lat": 52.1707992554, 599 | "lon": -106.699996948, 600 | "country": "加拿大", 601 | "cca2": "CA", 602 | "region": "北美洲", 603 | "city": "萨斯卡通" 604 | }, 605 | { 606 | "iata": "ARI", 607 | "lat": -18.348611, 608 | "lon": -70.338889, 609 | "country": "智利", 610 | "cca2": "CL", 611 | "region": "南美", 612 | "city": "阿里卡" 613 | }, 614 | { 615 | "iata": "CCP", 616 | "lat": -36.8201, 617 | "lon": -73.0444, 618 | "country": "智利", 619 | "cca2": "CL", 620 | "region": "南美", 621 | "city": "康塞普西翁" 622 | }, 623 | { 624 | "iata": "SCL", 625 | "lat": -33.3930015564, 626 | "lon": -70.7857971191, 627 | "country": "智利", 628 | "cca2": "CL", 629 | "region": "南美", 630 | "city": "圣地亚哥" 631 | }, 632 | { 633 | "iata": "BOG", 634 | "lat": 4.70159, 635 | "lon": -74.1469, 636 | "country": "哥伦比亚", 637 | "cca2": "CO", 638 | "region": "南美", 639 | "city": "波哥大" 640 | }, 641 | { 642 | "iata": "MDE", 643 | "lat": 6.16454, 644 | "lon": -75.4231, 645 | "country": "哥伦比亚", 646 | "cca2": "CO", 647 | "region": "南美", 648 | "city": "麦德林" 649 | }, 650 | { 651 | "iata": "FIH", 652 | "lat": -4.3857498169, 653 | "lon": 15.4446001053, 654 | "country": "刚果", 655 | "cca2": "CD", 656 | "region": "非洲", 657 | "city": "金沙萨" 658 | }, 659 | { 660 | "iata": "SJO", 661 | "lat": 9.9938602448, 662 | "lon": -84.2088012695, 663 | "country": "哥斯达黎加", 664 | "cca2": "CR", 665 | "region": "南美", 666 | "city": "圣何塞" 667 | }, 668 | { 669 | "iata": "ZAG", 670 | "lat": 45.7429008484, 671 | "lon": 16.0687999725, 672 | "country": "克罗地亚", 673 | "cca2": "HR", 674 | "region": "欧洲", 675 | "city": "萨格勒布" 676 | }, 677 | { 678 | "iata": "CUR", 679 | "lat": 12.1888999939, 680 | "lon": -68.9598007202, 681 | "country": "库拉索岛", 682 | "cca2": "CW", 683 | "region": "南美", 684 | "city": "库拉索岛" 685 | }, 686 | { 687 | "iata": "LCA", 688 | "lat": 34.8750991821, 689 | "lon": 33.6249008179, 690 | "country": "塞浦路斯", 691 | "cca2": "CY", 692 | "region": "欧洲", 693 | "city": "尼科西亚" 694 | }, 695 | { 696 | "iata": "PRG", 697 | "lat": 50.1007995605, 698 | "lon": 14.2600002289, 699 | "country": "捷克", 700 | "cca2": "CZ", 701 | "region": "欧洲", 702 | "city": "布拉格" 703 | }, 704 | { 705 | "iata": "CPH", 706 | "lat": 55.6179008484, 707 | "lon": 12.6560001373, 708 | "country": "丹麦", 709 | "cca2": "DK", 710 | "region": "欧洲", 711 | "city": "哥本哈根" 712 | }, 713 | { 714 | "iata": "JIB", 715 | "lat": 11.5473003387, 716 | "lon": 43.1595001221, 717 | "country": "吉布提", 718 | "cca2": "DJ", 719 | "region": "非洲", 720 | "city": "吉布提" 721 | }, 722 | { 723 | "iata": "SDQ", 724 | "lat": 18.4297008514, 725 | "lon": -69.6688995361, 726 | "country": "多米尼加", 727 | "cca2": "DO", 728 | "region": "北美洲", 729 | "city": "圣多明各" 730 | }, 731 | { 732 | "iata": "GYE", 733 | "lat": -2.1894, 734 | "lon": -79.8891, 735 | "country": "厄瓜多尔", 736 | "cca2": "EC", 737 | "region": "南美", 738 | "city": "瓜亚基尔" 739 | }, 740 | { 741 | "iata": "UIO", 742 | "lat": -0.1291666667, 743 | "lon": -78.3575, 744 | "country": "厄瓜多尔", 745 | "cca2": "EC", 746 | "region": "南美", 747 | "city": "基多" 748 | }, 749 | { 750 | "iata": "TLL", 751 | "lat": 59.4132995605, 752 | "lon": 24.8327999115, 753 | "country": "爱沙尼亚", 754 | "cca2": "EE", 755 | "region": "欧洲", 756 | "city": "塔林" 757 | }, 758 | { 759 | "iata": "HEL", 760 | "lat": 60.317199707, 761 | "lon": 24.963300705, 762 | "country": "芬兰", 763 | "cca2": "FI", 764 | "region": "欧洲", 765 | "city": "赫尔辛基" 766 | }, 767 | { 768 | "iata": "LYS", 769 | "lat": 45.7263, 770 | "lon": 5.0908, 771 | "country": "法国", 772 | "cca2": "FR", 773 | "region": "欧洲", 774 | "city": "里昂" 775 | }, 776 | { 777 | "iata": "MRS", 778 | "lat": 43.439271922, 779 | "lon": 5.2214241028, 780 | "country": "法国", 781 | "cca2": "FR", 782 | "region": "欧洲", 783 | "city": "马赛" 784 | }, 785 | { 786 | "iata": "CDG", 787 | "lat": 49.0127983093, 788 | "lon": 2.5499999523, 789 | "country": "法国", 790 | "cca2": "FR", 791 | "region": "欧洲", 792 | "city": "巴黎" 793 | }, 794 | { 795 | "iata": "PPT", 796 | "lat": -17.5536994934, 797 | "lon": -149.606994629, 798 | "country": "玻利尼西亚", 799 | "cca2": "PF", 800 | "region": "大洋洲", 801 | "city": "塔希提岛" 802 | }, 803 | { 804 | "iata": "TBS", 805 | "lat": 41.6692008972, 806 | "lon": 44.95470047, 807 | "country": "格鲁吉亚", 808 | "cca2": "GE", 809 | "region": "欧洲", 810 | "city": "第比利斯" 811 | }, 812 | { 813 | "iata": "TXL", 814 | "lat": 52.5597000122, 815 | "lon": 13.2876996994, 816 | "country": "德国", 817 | "cca2": "DE", 818 | "region": "欧洲", 819 | "city": "柏林" 820 | }, 821 | { 822 | "iata": "DUS", 823 | "lat": 51.2895011902, 824 | "lon": 6.7667798996, 825 | "country": "德国", 826 | "cca2": "DE", 827 | "region": "欧洲", 828 | "city": "杜塞尔多夫" 829 | }, 830 | { 831 | "iata": "FRA", 832 | "lat": 50.0264015198, 833 | "lon": 8.543129921, 834 | "country": "德国", 835 | "cca2": "DE", 836 | "region": "欧洲", 837 | "city": "法兰克福" 838 | }, 839 | { 840 | "iata": "HAM", 841 | "lat": 53.6304016113, 842 | "lon": 9.9882297516, 843 | "country": "德国", 844 | "cca2": "DE", 845 | "region": "欧洲", 846 | "city": "汉堡" 847 | }, 848 | { 849 | "iata": "MUC", 850 | "lat": 48.3538017273, 851 | "lon": 11.7861003876, 852 | "country": "德国", 853 | "cca2": "DE", 854 | "region": "欧洲", 855 | "city": "慕尼黑" 856 | }, 857 | { 858 | "iata": "STR", 859 | "lat": 48.783333, 860 | "lon": 9.183333, 861 | "country": "德国", 862 | "cca2": "DE", 863 | "region": "欧洲", 864 | "city": "斯图加特" 865 | }, 866 | { 867 | "iata": "ACC", 868 | "lat": 5.614818, 869 | "lon": -0.205874, 870 | "country": "加纳", 871 | "cca2": "GH", 872 | "region": "非洲", 873 | "city": "阿克拉" 874 | }, 875 | { 876 | "iata": "ATH", 877 | "lat": 37.9364013672, 878 | "lon": 23.9444999695, 879 | "country": "希腊", 880 | "cca2": "GR", 881 | "region": "欧洲", 882 | "city": "雅典" 883 | }, 884 | { 885 | "iata": "SKG", 886 | "lat": 40.5196990967, 887 | "lon": 22.9708995819, 888 | "country": "希腊", 889 | "cca2": "GR", 890 | "region": "欧洲", 891 | "city": "塞萨洛尼基" 892 | }, 893 | { 894 | "iata": "GND", 895 | "lat": 12.007116, 896 | "lon": -61.7882288, 897 | "country": "格林纳达", 898 | "cca2": "GD", 899 | "region": "南美", 900 | "city": "圣乔治" 901 | }, 902 | { 903 | "iata": "GUM", 904 | "lat": 13.4834003448, 905 | "lon": 144.796005249, 906 | "country": "关岛", 907 | "cca2": "GU", 908 | "region": "亚太", 909 | "city": "阿加尼亚" 910 | }, 911 | { 912 | "iata": "GUA", 913 | "lat": 14.5832996368, 914 | "lon": -90.5274963379, 915 | "country": "危地马拉", 916 | "cca2": "GT", 917 | "region": "北美洲", 918 | "city": "危地马拉" 919 | }, 920 | { 921 | "iata": "GEO", 922 | "lat": 6.825648, 923 | "lon": -58.163756, 924 | "country": "圭亚那", 925 | "cca2": "GY", 926 | "region": "南美", 927 | "city": "乔治城" 928 | }, 929 | { 930 | "iata": "PAP", 931 | "lat": 18.5799999237, 932 | "lon": -72.2925033569, 933 | "country": "海地", 934 | "cca2": "HT", 935 | "region": "北美洲", 936 | "city": "太子港" 937 | }, 938 | { 939 | "iata": "TGU", 940 | "lat": 14.0608, 941 | "lon": -87.2172, 942 | "country": "洪都拉斯", 943 | "cca2": "HN", 944 | "region": "南美", 945 | "city": "洪都拉斯" 946 | }, 947 | { 948 | "iata": "HKG", 949 | "lat": 22.3089008331, 950 | "lon": 113.915000916, 951 | "country": "香港", 952 | "cca2": "HK", 953 | "region": "亚太", 954 | "city": "香港" 955 | }, 956 | { 957 | "iata": "BUD", 958 | "lat": 47.4369010925, 959 | "lon": 19.2555999756, 960 | "country": "匈牙利", 961 | "cca2": "HU", 962 | "region": "欧洲", 963 | "city": "布达佩斯" 964 | }, 965 | { 966 | "iata": "KEF", 967 | "lat": 63.9850006104, 968 | "lon": -22.6056003571, 969 | "country": "冰岛", 970 | "cca2": "IS", 971 | "region": "欧洲", 972 | "city": "雷克雅未克" 973 | }, 974 | { 975 | "iata": "AMD", 976 | "lat": 23.0225, 977 | "lon": 72.5714, 978 | "country": "印度", 979 | "cca2": "IN", 980 | "region": "亚太", 981 | "city": "艾哈迈达巴德" 982 | }, 983 | { 984 | "iata": "BLR", 985 | "lat": 13.7835719, 986 | "lon": 76.6165937, 987 | "country": "印度", 988 | "cca2": "IN", 989 | "region": "亚太", 990 | "city": "班加罗尔" 991 | }, 992 | { 993 | "iata": "BBI", 994 | "lat": 20.2961, 995 | "lon": 85.8245, 996 | "country": "印度", 997 | "cca2": "IN", 998 | "region": "亚太", 999 | "city": "布巴内斯瓦尔" 1000 | }, 1001 | { 1002 | "iata": "IXC", 1003 | "lat": 30.673500061, 1004 | "lon": 76.7884979248, 1005 | "country": "印度", 1006 | "region": "亚太", 1007 | "city": "昌迪加尔" 1008 | }, 1009 | { 1010 | "iata": "MAA", 1011 | "lat": 12.9900054932, 1012 | "lon": 80.1692962646, 1013 | "country": "印度", 1014 | "cca2": "IN", 1015 | "region": "亚太", 1016 | "city": "金奈" 1017 | }, 1018 | { 1019 | "iata": "HYD", 1020 | "lat": 17.2313175201, 1021 | "lon": 78.4298553467, 1022 | "country": "印度", 1023 | "cca2": "IN", 1024 | "region": "亚太", 1025 | "city": "海得拉巴" 1026 | }, 1027 | { 1028 | "iata": "CNN", 1029 | "lat": 11.915858, 1030 | "lon": 75.55094, 1031 | "country": "印度", 1032 | "cca2": "IN", 1033 | "region": "亚太", 1034 | "city": "坎纳诺尔" 1035 | }, 1036 | { 1037 | "iata": "KNU", 1038 | "lat": 26.4499, 1039 | "lon": 80.3319, 1040 | "country": "印度", 1041 | "cca2": "IN", 1042 | "region": "亚太", 1043 | "city": "坎普尔" 1044 | }, 1045 | { 1046 | "iata": "COK", 1047 | "lat": 9.9312, 1048 | "lon": 76.2673, 1049 | "country": "印度", 1050 | "cca2": "IN", 1051 | "region": "亚太", 1052 | "city": "高知城" 1053 | }, 1054 | { 1055 | "iata": "CCU", 1056 | "lat": 22.6476933, 1057 | "lon": 88.4349249, 1058 | "country": "印度", 1059 | "cca2": "IN", 1060 | "region": "亚太", 1061 | "city": "加尔各答" 1062 | }, 1063 | { 1064 | "iata": "BOM", 1065 | "lat": 19.0886993408, 1066 | "lon": 72.8678970337, 1067 | "country": "印度", 1068 | "cca2": "IN", 1069 | "region": "亚太", 1070 | "city": "孟买" 1071 | }, 1072 | { 1073 | "iata": "NAG", 1074 | "lat": 21.1610714, 1075 | "lon": 79.0024702, 1076 | "country": "印度", 1077 | "cca2": "IN", 1078 | "region": "亚太", 1079 | "city": "那格浦尔" 1080 | }, 1081 | { 1082 | "iata": "DEL", 1083 | "lat": 28.5664997101, 1084 | "lon": 77.1031036377, 1085 | "country": "印度", 1086 | "cca2": "IN", 1087 | "region": "亚太", 1088 | "city": "新德里" 1089 | }, 1090 | { 1091 | "iata": "PAT", 1092 | "lat": 25.591299057, 1093 | "lon": 85.0879974365, 1094 | "country": "印度", 1095 | "cca2": "IN", 1096 | "region": "亚太", 1097 | "city": "巴特那" 1098 | }, 1099 | { 1100 | "iata": "DPS", 1101 | "lat": -8.748169899, 1102 | "lon": 115.1669998169, 1103 | "country": "印尼", 1104 | "cca2": "ID", 1105 | "region": "亚太", 1106 | "city": "登巴萨" 1107 | }, 1108 | { 1109 | "iata": "CGK", 1110 | "lat": -6.1275229, 1111 | "lon": 106.6515118, 1112 | "country": "印尼", 1113 | "cca2": "ID", 1114 | "region": "亚太", 1115 | "city": "雅加达" 1116 | }, 1117 | { 1118 | "iata": "JOG", 1119 | "lat": -7.7881798744, 1120 | "lon": 110.4319992065, 1121 | "country": "印尼", 1122 | "cca2": "ID", 1123 | "region": "亚太", 1124 | "city": "日惹特区" 1125 | }, 1126 | { 1127 | "iata": "BGW", 1128 | "lat": 33.2625007629, 1129 | "lon": 44.2346000671, 1130 | "country": "伊拉克", 1131 | "cca2": "IQ", 1132 | "region": "中东", 1133 | "city": "巴格达" 1134 | }, 1135 | { 1136 | "iata": "BSR", 1137 | "lat": 30.5491008759, 1138 | "lon": 47.6621017456, 1139 | "country": "伊拉克", 1140 | "cca2": "IQ", 1141 | "region": "中东", 1142 | "city": "巴士拉" 1143 | }, 1144 | { 1145 | "iata": "EBL", 1146 | "lat": 36.1901, 1147 | "lon": 43.993, 1148 | "country": "伊拉克", 1149 | "cca2": "IQ", 1150 | "region": "中东", 1151 | "city": "阿尔比尔" 1152 | }, 1153 | { 1154 | "iata": "NJF", 1155 | "lat": 31.989722, 1156 | "lon": 44.404167, 1157 | "country": "伊拉克", 1158 | "cca2": "IQ", 1159 | "region": "中东", 1160 | "city": "纳杰夫" 1161 | }, 1162 | { 1163 | "iata": "XNH", 1164 | "lat": 30.9358005524, 1165 | "lon": 46.0900993347, 1166 | "country": "伊拉克", 1167 | "cca2": "IQ", 1168 | "region": "中东", 1169 | "city": "纳西里耶" 1170 | }, 1171 | { 1172 | "iata": "ISU", 1173 | "lat": 35.5668, 1174 | "lon": 45.4161, 1175 | "country": "伊拉克", 1176 | "cca2": "IQ", 1177 | "region": "中东", 1178 | "city": "苏莱曼尼亚" 1179 | }, 1180 | { 1181 | "iata": "ORK", 1182 | "lat": 51.8413009644, 1183 | "lon": -8.491109848, 1184 | "country": "爱尔兰", 1185 | "cca2": "IE", 1186 | "region": "欧洲", 1187 | "city": "科克" 1188 | }, 1189 | { 1190 | "iata": "DUB", 1191 | "lat": 53.4212989807, 1192 | "lon": -6.270070076, 1193 | "country": "爱尔兰", 1194 | "cca2": "IE", 1195 | "region": "欧洲", 1196 | "city": "都柏林" 1197 | }, 1198 | { 1199 | "iata": "HFA", 1200 | "lat": 32.78492, 1201 | "lon": 34.96069, 1202 | "country": "以色列", 1203 | "cca2": "IL", 1204 | "region": "中东", 1205 | "city": "海法" 1206 | }, 1207 | { 1208 | "iata": "TLV", 1209 | "lat": 32.0113983154, 1210 | "lon": 34.8866996765, 1211 | "country": "以色列", 1212 | "cca2": "IL", 1213 | "region": "中东", 1214 | "city": "特拉维夫" 1215 | }, 1216 | { 1217 | "iata": "MXP", 1218 | "lat": 45.6305999756, 1219 | "lon": 8.7281103134, 1220 | "country": "意大利", 1221 | "cca2": "IT", 1222 | "region": "欧洲", 1223 | "city": "米兰" 1224 | }, 1225 | { 1226 | "iata": "PMO", 1227 | "lat": 38.16114, 1228 | "lon": 13.31546, 1229 | "country": "意大利", 1230 | "cca2": "IT", 1231 | "region": "欧洲", 1232 | "city": "巴勒莫" 1233 | }, 1234 | { 1235 | "iata": "FCO", 1236 | "lat": 41.8045005798, 1237 | "lon": 12.2508001328, 1238 | "country": "意大利", 1239 | "cca2": "IT", 1240 | "region": "欧洲", 1241 | "city": "罗马" 1242 | }, 1243 | { 1244 | "iata": "KIN", 1245 | "lat": 17.9951, 1246 | "lon": -76.7846, 1247 | "country": "牙买加", 1248 | "cca2": "JM", 1249 | "region": "北美洲", 1250 | "city": "金斯顿" 1251 | }, 1252 | { 1253 | "iata": "FUK", 1254 | "lat": 33.5902, 1255 | "lon": 130.4017, 1256 | "country": "日本", 1257 | "cca2": "JP", 1258 | "region": "亚太", 1259 | "city": "福冈" 1260 | }, 1261 | { 1262 | "iata": "OKA", 1263 | "lat": 26.1958, 1264 | "lon": 127.646, 1265 | "country": "日本", 1266 | "cca2": "JP", 1267 | "region": "亚太", 1268 | "city": "那霸" 1269 | }, 1270 | { 1271 | "iata": "KIX", 1272 | "lat": 34.4272994995, 1273 | "lon": 135.244003296, 1274 | "country": "日本", 1275 | "cca2": "JP", 1276 | "region": "亚太", 1277 | "city": "大阪" 1278 | }, 1279 | { 1280 | "iata": "NRT", 1281 | "lat": 35.7647018433, 1282 | "lon": 140.386001587, 1283 | "country": "日本", 1284 | "cca2": "JP", 1285 | "region": "亚太", 1286 | "city": "东京" 1287 | }, 1288 | { 1289 | "iata": "AMM", 1290 | "lat": 31.7226009369, 1291 | "lon": 35.9931983948, 1292 | "country": "约旦", 1293 | "cca2": "JO", 1294 | "region": "中东", 1295 | "city": "安曼" 1296 | }, 1297 | { 1298 | "iata": "ALA", 1299 | "lat": 43.3521003723, 1300 | "lon": 77.0404968262, 1301 | "country": "哈萨克斯坦", 1302 | "cca2": "KZ", 1303 | "region": "亚太", 1304 | "city": "阿拉木图" 1305 | }, 1306 | { 1307 | "iata": "MBA", 1308 | "lat": -4.0348300934, 1309 | "lon": 39.5942001343, 1310 | "country": "肯尼亚", 1311 | "cca2": "KE", 1312 | "region": "非洲", 1313 | "city": "蒙巴萨" 1314 | }, 1315 | { 1316 | "iata": "NBO", 1317 | "lat": -1.319239974, 1318 | "lon": 36.9277992249, 1319 | "country": "肯尼亚", 1320 | "cca2": "KE", 1321 | "region": "非洲", 1322 | "city": "内罗毕" 1323 | }, 1324 | { 1325 | "iata": "ICN", 1326 | "lat": 37.4691009521, 1327 | "lon": 126.450996399, 1328 | "country": "韩国", 1329 | "cca2": "KR", 1330 | "region": "亚太", 1331 | "city": "首尔" 1332 | }, 1333 | { 1334 | "iata": "KWI", 1335 | "lat": 29.226600647, 1336 | "lon": 47.9688987732, 1337 | "cca2": "科威特", 1338 | "cca2": "KW", 1339 | "region": "中东", 1340 | "city": "科威特城" 1341 | }, 1342 | { 1343 | "iata": "VTE", 1344 | "lat": 17.9757, 1345 | "lon": 102.5683, 1346 | "country": "老挝", 1347 | "cca2": "LA", 1348 | "region": "亚太", 1349 | "city": "万象" 1350 | }, 1351 | { 1352 | "iata": "RIX", 1353 | "lat": 56.9235992432, 1354 | "lon": 23.9710998535, 1355 | "country": "拉脱维亚", 1356 | "cca2": "LV", 1357 | "region": "欧洲", 1358 | "city": "里加" 1359 | }, 1360 | { 1361 | "iata": "BEY", 1362 | "lat": 33.8208999634, 1363 | "lon": 35.4883995056, 1364 | "country": "黎巴嫩", 1365 | "cca2": "LB", 1366 | "region": "中东", 1367 | "city": "贝鲁特" 1368 | }, 1369 | { 1370 | "iata": "VNO", 1371 | "lat": 54.6341018677, 1372 | "lon": 25.2858009338, 1373 | "country": "立陶宛", 1374 | "cca2": "LT", 1375 | "region": "欧洲", 1376 | "city": "维尔纽斯" 1377 | }, 1378 | { 1379 | "iata": "LUX", 1380 | "lat": 49.6265983582, 1381 | "lon": 6.211520195, 1382 | "country": "卢森堡", 1383 | "cca2": "LU", 1384 | "region": "欧洲", 1385 | "city": "卢森堡" 1386 | }, 1387 | { 1388 | "iata": "MFM", 1389 | "lat": 22.1495990753, 1390 | "lon": 113.592002869, 1391 | "country": "澳门", 1392 | "cca2": "MO", 1393 | "region": "亚太", 1394 | "city": "澳门" 1395 | }, 1396 | { 1397 | "iata": "TNR", 1398 | "lat": -18.91368, 1399 | "lon": 47.53613, 1400 | "country": "马达加斯加", 1401 | "cca2": "MG", 1402 | "region": "非洲", 1403 | "city": "塔那那利佛" 1404 | }, 1405 | { 1406 | "iata": "JHB", 1407 | "lat": 1.635848, 1408 | "lon": 103.665943, 1409 | "country": "马来西亚", 1410 | "cca2": "MY", 1411 | "region": "亚太", 1412 | "city": "柔佛州" 1413 | }, 1414 | { 1415 | "iata": "KUL", 1416 | "lat": 2.745579958, 1417 | "lon": 101.709999084, 1418 | "country": "马来西亚", 1419 | "cca2": "MY", 1420 | "region": "亚太", 1421 | "city": "吉隆坡" 1422 | }, 1423 | { 1424 | "iata": "MLE", 1425 | "lat": 4.1748, 1426 | "lon": 73.50888, 1427 | "country": "马尔代夫", 1428 | "cca2": "MV", 1429 | "region": "亚太", 1430 | "city": "马累" 1431 | }, 1432 | { 1433 | "iata": "MRU", 1434 | "lat": -20.4302005768, 1435 | "lon": 57.6836013794, 1436 | "country": "毛里求斯", 1437 | "cca2": "MU", 1438 | "region": "非洲", 1439 | "city": "路易港" 1440 | }, 1441 | { 1442 | "iata": "GDL", 1443 | "lat": 20.5217990875, 1444 | "lon": -103.3109970093, 1445 | "country": "墨西哥", 1446 | "cca2": "MX", 1447 | "region": "北美洲", 1448 | "city": "瓜达拉哈拉" 1449 | }, 1450 | { 1451 | "iata": "MEX", 1452 | "lat": 19.4363002777, 1453 | "lon": -99.0720977783, 1454 | "country": "墨西哥", 1455 | "cca2": "MX", 1456 | "region": "北美洲", 1457 | "city": "墨西哥" 1458 | }, 1459 | { 1460 | "iata": "QRO", 1461 | "lat": 20.6173000336, 1462 | "lon": -100.185997009, 1463 | "country": "墨西哥", 1464 | "cca2": "MX", 1465 | "region": "北美洲", 1466 | "city": "克雷塔羅" 1467 | }, 1468 | { 1469 | "iata": "KIV", 1470 | "lat": 46.9277000427, 1471 | "lon": 28.9309997559, 1472 | "country": "摩尔多瓦", 1473 | "cca2": "MD", 1474 | "region": "欧洲", 1475 | "city": "基希讷乌" 1476 | }, 1477 | { 1478 | "iata": "ULN", 1479 | "lat": 47.8431015015, 1480 | "lon": 106.766998291, 1481 | "country": "蒙古", 1482 | "cca2": "MN", 1483 | "region": "亚太", 1484 | "city": "蒙古" 1485 | }, 1486 | { 1487 | "iata": "CMN", 1488 | "lat": 33.3675003052, 1489 | "lon": -7.5899701118, 1490 | "country": "摩洛哥", 1491 | "cca2": "MA", 1492 | "region": "非洲", 1493 | "city": "卡萨布兰卡" 1494 | }, 1495 | { 1496 | "iata": "MPM", 1497 | "lat": -25.9207992554, 1498 | "lon": 32.5726013184, 1499 | "country": "莫桑比克", 1500 | "cca2": "MZ", 1501 | "region": "非洲", 1502 | "city": "马普托" 1503 | }, 1504 | { 1505 | "iata": "MDL", 1506 | "lat": 21.7051697, 1507 | "lon": 95.9695206, 1508 | "country": "缅甸", 1509 | "cca2": "MM", 1510 | "region": "亚太", 1511 | "city": "曼德勒" 1512 | }, 1513 | { 1514 | "iata": "RGN", 1515 | "lat": 16.9073009491, 1516 | "lon": 96.1332015991, 1517 | "country": "缅甸", 1518 | "cca2": "MM", 1519 | "region": "亚太", 1520 | "city": "仰光" 1521 | }, 1522 | { 1523 | "iata": "KTM", 1524 | "lat": 27.6965999603, 1525 | "lon": 85.3591003418, 1526 | "country": "尼泊尔", 1527 | "cca2": "NP", 1528 | "region": "亚太", 1529 | "city": "加德满都" 1530 | }, 1531 | { 1532 | "iata": "AMS", 1533 | "lat": 52.3086013794, 1534 | "lon": 4.7638897896, 1535 | "country": "荷兰", 1536 | "cca2": "NL", 1537 | "region": "欧洲", 1538 | "city": "阿姆斯特丹" 1539 | }, 1540 | { 1541 | "iata": "NOU", 1542 | "lat": -22.0146007538, 1543 | "lon": 166.212997436, 1544 | "country": "新喀里多尼亚", 1545 | "cca2": "NC", 1546 | "region": "大洋洲", 1547 | "city": "努美阿" 1548 | }, 1549 | { 1550 | "iata": "AKL", 1551 | "lat": -37.0080986023, 1552 | "lon": 174.792007446, 1553 | "country": "新西兰", 1554 | "cca2": "NZ", 1555 | "region": "大洋洲", 1556 | "city": "奥克兰" 1557 | }, 1558 | { 1559 | "iata": "CHC", 1560 | "lat": -43.4893989563, 1561 | "lon": 172.5319976807, 1562 | "country": "新西兰", 1563 | "cca2": "NZ", 1564 | "region": "大洋洲", 1565 | "city": "克赖斯特彻" 1566 | }, 1567 | { 1568 | "iata": "LOS", 1569 | "lat": 6.5773701668, 1570 | "lon": 3.321160078, 1571 | "country": "尼日利亚", 1572 | "cca2": "NG", 1573 | "region": "非洲", 1574 | "city": "拉各斯" 1575 | }, 1576 | { 1577 | "iata": "OSL", 1578 | "lat": 60.193901062, 1579 | "lon": 11.100399971, 1580 | "country": "挪威", 1581 | "cca2": "NO", 1582 | "region": "欧洲", 1583 | "city": "奥斯陆" 1584 | }, 1585 | { 1586 | "iata": "MCT", 1587 | "lat": 23.5932998657, 1588 | "lon": 58.2844009399, 1589 | "country": "阿曼", 1590 | "cca2": "OM", 1591 | "region": "中东", 1592 | "city": "马斯喀特" 1593 | }, 1594 | { 1595 | "iata": "ISB", 1596 | "lat": 33.6166992188, 1597 | "lon": 73.0991973877, 1598 | "country": "巴基斯坦", 1599 | "cca2": "PK", 1600 | "region": "亚太", 1601 | "city": "伊斯兰堡" 1602 | }, 1603 | { 1604 | "iata": "KHI", 1605 | "lat": 24.9064998627, 1606 | "lon": 67.1607971191, 1607 | "country": "巴基斯坦", 1608 | "cca2": "PK", 1609 | "region": "亚太", 1610 | "city": "卡拉奇" 1611 | }, 1612 | { 1613 | "iata": "LHE", 1614 | "lat": 31.5216007233, 1615 | "lon": 74.4036026001, 1616 | "country": "巴基斯坦", 1617 | "cca2": "PK", 1618 | "region": "亚太", 1619 | "city": "拉合尔" 1620 | }, 1621 | { 1622 | "iata": "ZDM", 1623 | "lat": 32.2719, 1624 | "lon": 35.0194, 1625 | "country": "巴勒斯坦", 1626 | "cca2": "PS", 1627 | "region": "中东", 1628 | "city": "拉姆安拉" 1629 | }, 1630 | { 1631 | "iata": "PTY", 1632 | "lat": 9.0713596344, 1633 | "lon": -79.3834991455, 1634 | "country": "巴拿马", 1635 | "cca2": "PA", 1636 | "region": "南美", 1637 | "city": "巴拿马城" 1638 | }, 1639 | { 1640 | "iata": "ASU", 1641 | "lat": -25.2399997711, 1642 | "lon": -57.5200004578, 1643 | "country": "巴拉圭", 1644 | "cca2": "PY", 1645 | "region": "南美", 1646 | "city": "亚松森" 1647 | }, 1648 | { 1649 | "iata": "LIM", 1650 | "lat": -12.021900177, 1651 | "lon": -77.1143035889, 1652 | "country": "秘鲁", 1653 | "cca2": "PE", 1654 | "region": "南美", 1655 | "city": "利马" 1656 | }, 1657 | { 1658 | "iata": "CGY", 1659 | "lat": 8.4156198502, 1660 | "lon": 124.611000061, 1661 | "country": "菲律宾", 1662 | "cca2": "PH", 1663 | "region": "亚太", 1664 | "city": "哥打巴托市" 1665 | }, 1666 | { 1667 | "iata": "CEB", 1668 | "lat": 10.3074998856, 1669 | "lon": 123.978996277, 1670 | "country": "菲律宾", 1671 | "cca2": "PH", 1672 | "region": "亚太", 1673 | "city": "宿务" 1674 | }, 1675 | { 1676 | "iata": "MNL", 1677 | "lat": 14.508600235, 1678 | "lon": 121.019996643, 1679 | "country": "菲律宾", 1680 | "cca2": "PH", 1681 | "region": "亚太", 1682 | "city": "马尼拉" 1683 | }, 1684 | { 1685 | "iata": "WAW", 1686 | "lat": 52.1656990051, 1687 | "lon": 20.9671001434, 1688 | "country": "波兰", 1689 | "cca2": "PL", 1690 | "region": "欧洲", 1691 | "city": "华沙" 1692 | }, 1693 | { 1694 | "iata": "LIS", 1695 | "lat": 38.7812995911, 1696 | "lon": -9.1359195709, 1697 | "country": "葡萄牙", 1698 | "cca2": "PT", 1699 | "region": "欧洲", 1700 | "city": "里斯本" 1701 | }, 1702 | { 1703 | "iata": "DOH", 1704 | "lat": 25.2605946, 1705 | "lon": 51.6137665, 1706 | "country": "卡塔尔", 1707 | "cca2": "QA", 1708 | "region": "中东", 1709 | "city": "多哈" 1710 | }, 1711 | { 1712 | "iata": "RUN", 1713 | "lat": -20.8871002197, 1714 | "lon": 55.5102996826, 1715 | "country": "留尼汪", 1716 | "cca2": "RE", 1717 | "region": "非洲", 1718 | "city": "圣但尼" 1719 | }, 1720 | { 1721 | "iata": "OTP", 1722 | "lat": 44.5722007751, 1723 | "lon": 26.1021995544, 1724 | "country": "罗马尼亚", 1725 | "cca2": "RO", 1726 | "region": "欧洲", 1727 | "city": "布加勒斯特" 1728 | }, 1729 | { 1730 | "iata": "KHV", 1731 | "lat": 48.5279998779, 1732 | "lon": 135.18800354, 1733 | "country": "俄罗斯", 1734 | "cca2": "RU", 1735 | "region": "亚太", 1736 | "city": "哈巴罗夫斯克" 1737 | }, 1738 | { 1739 | "iata": "KJA", 1740 | "lat": 56.0153, 1741 | "lon": 92.8932, 1742 | "country": "俄罗斯", 1743 | "cca2": "RU", 1744 | "region": "亚太", 1745 | "city": "克拉斯诺亚尔斯克" 1746 | }, 1747 | { 1748 | "iata": "DME", 1749 | "lat": 55.4087982178, 1750 | "lon": 37.9062995911, 1751 | "country": "俄罗斯", 1752 | "cca2": "RU", 1753 | "region": "欧洲", 1754 | "city": "莫斯科" 1755 | }, 1756 | { 1757 | "iata": "LED", 1758 | "lat": 59.8003005981, 1759 | "lon": 30.2625007629, 1760 | "country": "俄罗斯", 1761 | "cca2": "RU", 1762 | "region": "欧洲", 1763 | "city": "圣彼得堡" 1764 | }, 1765 | { 1766 | "iata": "KLD", 1767 | "lat": 56.8587, 1768 | "lon": 35.9176, 1769 | "country": "俄罗斯", 1770 | "cca2": "RU", 1771 | "region": "欧洲", 1772 | "city": "特维尔" 1773 | }, 1774 | { 1775 | "iata": "SVX", 1776 | "lat": 56.8431, 1777 | "lon": 60.6454, 1778 | "country": "俄罗斯", 1779 | "cca2": "RU", 1780 | "region": "亚太", 1781 | "city": "叶卡捷琳堡" 1782 | }, 1783 | { 1784 | "iata": "KGL", 1785 | "lat": -1.9686299563, 1786 | "lon": 30.1394996643, 1787 | "country": "卢旺达", 1788 | "cca2": "RW", 1789 | "region": "非洲", 1790 | "city": "基加利" 1791 | }, 1792 | { 1793 | "iata": "DMM", 1794 | "lat": 26.471200943, 1795 | "lon": 49.7979011536, 1796 | "country": "沙特阿拉伯", 1797 | "cca2": "SA", 1798 | "region": "中东", 1799 | "city": "达曼" 1800 | }, 1801 | { 1802 | "iata": "JED", 1803 | "lat": 21.679599762, 1804 | "lon": 39.15650177, 1805 | "country": "沙特阿拉伯", 1806 | "cca2": "SA", 1807 | "region": "中东", 1808 | "city": "吉达" 1809 | }, 1810 | { 1811 | "iata": "RUH", 1812 | "lat": 24.9575996399, 1813 | "lon": 46.6987991333, 1814 | "country": "沙特阿拉伯", 1815 | "cca2": "SA", 1816 | "region": "中东", 1817 | "city": "利雅得" 1818 | }, 1819 | { 1820 | "iata": "DKR", 1821 | "lat": 14.7412099, 1822 | "lon": -17.4889771, 1823 | "country": "塞内加尔", 1824 | "cca2": "SN", 1825 | "region": "非洲", 1826 | "city": "达喀尔" 1827 | }, 1828 | { 1829 | "iata": "BEG", 1830 | "lat": 44.8184013367, 1831 | "lon": 20.3090991974, 1832 | "country": "塞尔维亚", 1833 | "cca2": "RS", 1834 | "region": "欧洲", 1835 | "city": "贝尔格莱德" 1836 | }, 1837 | { 1838 | "iata": "SIN", 1839 | "lat": 1.3501900434, 1840 | "lon": 103.994003296, 1841 | "country": "新加坡", 1842 | "cca2": "SG", 1843 | "region": "亚太", 1844 | "city": "新加坡" 1845 | }, 1846 | { 1847 | "iata": "BTS", 1848 | "lat": 48.1486, 1849 | "lon": 17.1077, 1850 | "country": "斯洛伐克", 1851 | "cca2": "SK", 1852 | "region": "欧洲", 1853 | "city": "布拉迪斯拉发" 1854 | }, 1855 | { 1856 | "iata": "CPT", 1857 | "lat": -33.9648017883, 1858 | "lon": 18.6016998291, 1859 | "country": "南非", 1860 | "cca2": "ZA", 1861 | "region": "非洲", 1862 | "city": "开普敦" 1863 | }, 1864 | { 1865 | "iata": "DUR", 1866 | "lat": -29.6144444444, 1867 | "lon": 31.1197222222, 1868 | "country": "南非", 1869 | "cca2": "ZA", 1870 | "region": "非洲", 1871 | "city": "德班" 1872 | }, 1873 | { 1874 | "iata": "JNB", 1875 | "lat": -26.133333, 1876 | "lon": 28.25, 1877 | "country": "南非", 1878 | "cca2": "ZA", 1879 | "region": "非洲", 1880 | "city": "约翰内斯堡" 1881 | }, 1882 | { 1883 | "iata": "BCN", 1884 | "lat": 41.2971000671, 1885 | "lon": 2.0784599781, 1886 | "country": "西班牙", 1887 | "cca2": "ES", 1888 | "region": "欧洲", 1889 | "city": "巴塞罗那" 1890 | }, 1891 | { 1892 | "iata": "MAD", 1893 | "lat": 40.4936, 1894 | "lon": -3.56676, 1895 | "country": "西班牙", 1896 | "cca2": "ES", 1897 | "region": "欧洲", 1898 | "city": "马德里" 1899 | }, 1900 | { 1901 | "iata": "CMB", 1902 | "lat": 7.1807599068, 1903 | "lon": 79.8841018677, 1904 | "country": "斯里兰卡", 1905 | "cca2": "LK", 1906 | "region": "亚太", 1907 | "city": "科伦坡" 1908 | }, 1909 | { 1910 | "iata": "PBM", 1911 | "lat": 5.452831, 1912 | "lon": -55.187783, 1913 | "country": "苏里南", 1914 | "cca2": "SR", 1915 | "region": "南美", 1916 | "city": "帕拉马里博" 1917 | }, 1918 | { 1919 | "iata": "GOT", 1920 | "lat": 57.6627998352, 1921 | "lon": 12.279800415, 1922 | "country": "瑞典", 1923 | "cca2": "SE", 1924 | "region": "欧洲", 1925 | "city": "哥德堡" 1926 | }, 1927 | { 1928 | "iata": "ARN", 1929 | "lat": 59.6519012451, 1930 | "lon": 17.9186000824, 1931 | "country": "瑞典", 1932 | "cca2": "SE", 1933 | "region": "欧洲", 1934 | "city": "斯德哥尔摩" 1935 | }, 1936 | { 1937 | "iata": "GVA", 1938 | "lat": 46.2380981445, 1939 | "lon": 6.1089501381, 1940 | "country": "瑞士", 1941 | "cca2": "CH", 1942 | "region": "欧洲", 1943 | "city": "日内瓦" 1944 | }, 1945 | { 1946 | "iata": "ZRH", 1947 | "lat": 47.4646987915, 1948 | "lon": 8.5491695404, 1949 | "country": "瑞士", 1950 | "cca2": "CH", 1951 | "region": "欧洲", 1952 | "city": "苏黎世" 1953 | }, 1954 | { 1955 | "iata": "KHH", 1956 | "lat": 22.5771007538, 1957 | "lon": 120.3499984741, 1958 | "country": "台湾", 1959 | "cca2": "TW", 1960 | "region": "亚太", 1961 | "city": "高雄" 1962 | }, 1963 | { 1964 | "iata": "TPE", 1965 | "lat": 25.0776996613, 1966 | "lon": 121.233001709, 1967 | "country": "台湾", 1968 | "cca2": "TW", 1969 | "region": "亚太", 1970 | "city": "台北" 1971 | }, 1972 | { 1973 | "iata": "DAR", 1974 | "lat": -6.8781099319, 1975 | "lon": 39.2025985718, 1976 | "country": "坦桑尼亚", 1977 | "cca2": "TZ", 1978 | "region": "非洲", 1979 | "city": "达累斯萨拉姆" 1980 | }, 1981 | { 1982 | "iata": "BKK", 1983 | "lat": 13.6810998917, 1984 | "lon": 100.747001648, 1985 | "country": "泰国", 1986 | "cca2": "TH", 1987 | "region": "亚太", 1988 | "city": "曼谷" 1989 | }, 1990 | { 1991 | "iata": "CNX", 1992 | "lat": 18.7667999268, 1993 | "lon": 98.962600708, 1994 | "country": "泰国", 1995 | "cca2": "TH", 1996 | "region": "亚太", 1997 | "city": "清迈" 1998 | }, 1999 | { 2000 | "iata": "URT", 2001 | "lat": 9.1325998306, 2002 | "lon": 99.135597229, 2003 | "country": "泰国", 2004 | "cca2": "TH", 2005 | "region": "亚太", 2006 | "city": "素叻府" 2007 | }, 2008 | { 2009 | "iata": "TUN", 2010 | "lat": 36.8510017395, 2011 | "lon": 10.2271995544, 2012 | "country": "突尼斯", 2013 | "cca2": "TN", 2014 | "region": "非洲", 2015 | "city": "突尼斯" 2016 | }, 2017 | { 2018 | "iata": "IST", 2019 | "lat": 40.9768981934, 2020 | "lon": 28.8145999908, 2021 | "country": "土耳其", 2022 | "cca2": "TR", 2023 | "region": "欧洲", 2024 | "city": "伊斯坦布尔" 2025 | }, 2026 | { 2027 | "iata": "ADB", 2028 | "lat": 38.32377, 2029 | "lon": 27.14317, 2030 | "country": "土耳其", 2031 | "cca2": "TR", 2032 | "region": "欧洲", 2033 | "city": "伊兹密尔" 2034 | }, 2035 | { 2036 | "iata": "KBP", 2037 | "lat": 50.3450012207, 2038 | "lon": 30.8946990967, 2039 | "country": "乌克兰", 2040 | "cca2": "UA", 2041 | "region": "欧洲", 2042 | "city": "基辅" 2043 | }, 2044 | { 2045 | "iata": "DXB", 2046 | "lat": 25.2527999878, 2047 | "lon": 55.3643989563, 2048 | "country": "阿联酋", 2049 | "cca2": "AE", 2050 | "region": "中东", 2051 | "city": "迪拜" 2052 | }, 2053 | { 2054 | "iata": "EDI", 2055 | "lat": 55.9500007629, 2056 | "lon": -3.3724999428, 2057 | "country": "英国", 2058 | "cca2": "GB", 2059 | "region": "欧洲", 2060 | "city": "爱丁堡" 2061 | }, 2062 | { 2063 | "iata": "LHR", 2064 | "lat": 51.4706001282, 2065 | "lon": -0.4619410038, 2066 | "country": "英国", 2067 | "cca2": "GB", 2068 | "region": "欧洲", 2069 | "city": "伦敦" 2070 | }, 2071 | { 2072 | "iata": "MAN", 2073 | "lat": 53.3536987305, 2074 | "lon": -2.2749500275, 2075 | "country": "英国", 2076 | "cca2": "GB", 2077 | "region": "欧洲", 2078 | "city": "Manchester" 2079 | }, 2080 | { 2081 | "iata": "MGM", 2082 | "lat": 32.30059814, 2083 | "lon": -86.39399719, 2084 | "country": "美国", 2085 | "cca2": "US", 2086 | "region": "北美洲", 2087 | "city": "蒙哥马利" 2088 | }, 2089 | { 2090 | "iata": "PHX", 2091 | "lat": 33.434299469, 2092 | "lon": -112.012001038, 2093 | "country": "美国", 2094 | "cca2": "US", 2095 | "region": "北美洲", 2096 | "city": "凤凰城" 2097 | }, 2098 | { 2099 | "iata": "LAX", 2100 | "lat": 33.94250107, 2101 | "lon": -118.4079971, 2102 | "country": "美国", 2103 | "cca2": "US", 2104 | "region": "北美洲", 2105 | "city": "洛杉矶" 2106 | }, 2107 | { 2108 | "iata": "SMF", 2109 | "lat": 38.695400238, 2110 | "lon": -121.591003418, 2111 | "country": "美国", 2112 | "cca2": "US", 2113 | "region": "北美洲", 2114 | "city": "萨克拉门托" 2115 | }, 2116 | { 2117 | "iata": "SAN", 2118 | "lat": 32.7336006165, 2119 | "lon": -117.190002441, 2120 | "country": "美国", 2121 | "cca2": "US", 2122 | "region": "北美洲", 2123 | "city": "圣地亚哥" 2124 | }, 2125 | { 2126 | "iata": "SFO", 2127 | "lat": 37.6189994812, 2128 | "lon": -122.375, 2129 | "country": "美国", 2130 | "cca2": "US", 2131 | "region": "北美洲", 2132 | "city": "旧金山" 2133 | }, 2134 | { 2135 | "iata": "SJC", 2136 | "lat": 37.3625984192, 2137 | "lon": -121.929000855, 2138 | "country": "美国", 2139 | "cca2": "US", 2140 | "region": "北美洲", 2141 | "city": "圣何塞" 2142 | }, 2143 | { 2144 | "iata": "DEN", 2145 | "lat": 39.8616981506, 2146 | "lon": -104.672996521, 2147 | "country": "美国", 2148 | "cca2": "US", 2149 | "region": "北美洲", 2150 | "city": "丹佛" 2151 | }, 2152 | { 2153 | "iata": "JAX", 2154 | "lat": 30.4941005707, 2155 | "lon": -81.6878967285, 2156 | "country": "美国", 2157 | "cca2": "US", 2158 | "region": "北美洲", 2159 | "city": "杰克逊维尔" 2160 | }, 2161 | { 2162 | "iata": "MIA", 2163 | "lat": 25.7931995392, 2164 | "lon": -80.2906036377, 2165 | "country": "美国", 2166 | "cca2": "US", 2167 | "region": "北美洲", 2168 | "city": "迈阿密" 2169 | }, 2170 | { 2171 | "iata": "TLH", 2172 | "lat": 30.3964996338, 2173 | "lon": -84.3503036499, 2174 | "country": "美国", 2175 | "cca2": "US", 2176 | "region": "北美洲", 2177 | "city": "塔拉哈西" 2178 | }, 2179 | { 2180 | "iata": "TPA", 2181 | "lat": 27.9755001068, 2182 | "lon": -82.533203125, 2183 | "country": "美国", 2184 | "cca2": "US", 2185 | "region": "北美洲", 2186 | "city": "坦帕市" 2187 | }, 2188 | { 2189 | "iata": "ATL", 2190 | "lat": 33.6366996765, 2191 | "lon": -84.4281005859, 2192 | "country": "美国", 2193 | "cca2": "US", 2194 | "region": "北美洲", 2195 | "city": "亚特兰大" 2196 | }, 2197 | { 2198 | "iata": "HNL", 2199 | "lat": 21.3187007904, 2200 | "lon": -157.9219970703, 2201 | "country": "美国", 2202 | "cca2": "US", 2203 | "region": "北美洲", 2204 | "city": "檀香山" 2205 | }, 2206 | { 2207 | "iata": "ORD", 2208 | "lat": 41.97859955, 2209 | "lon": -87.90480042, 2210 | "country": "美国", 2211 | "cca2": "US", 2212 | "region": "北美洲", 2213 | "city": "芝加哥" 2214 | }, 2215 | { 2216 | "iata": "IND", 2217 | "lat": 39.717300415, 2218 | "lon": -86.2944030762, 2219 | "country": "美国", 2220 | "cca2": "US", 2221 | "region": "北美洲", 2222 | "city": "印第安纳波利斯" 2223 | }, 2224 | { 2225 | "iata": "BGR", 2226 | "lat": 44.8081, 2227 | "lon": -68.795, 2228 | "country": "美国", 2229 | "cca2": "US", 2230 | "region": "北美洲", 2231 | "city": "班格尔" 2232 | }, 2233 | { 2234 | "iata": "BOS", 2235 | "lat": 42.36429977, 2236 | "lon": -71.00520325, 2237 | "country": "美国", 2238 | "cca2": "US", 2239 | "region": "北美洲", 2240 | "city": "波士顿" 2241 | }, 2242 | { 2243 | "iata": "DTW", 2244 | "lat": 42.2123985291, 2245 | "lon": -83.3534011841, 2246 | "country": "美国", 2247 | "cca2": "US", 2248 | "region": "北美洲", 2249 | "city": "底特律" 2250 | }, 2251 | { 2252 | "iata": "MSP", 2253 | "lat": 44.8819999695, 2254 | "lon": -93.2218017578, 2255 | "country": "美国", 2256 | "cca2": "US", 2257 | "region": "北美洲", 2258 | "city": "明尼阿波利斯" 2259 | }, 2260 | { 2261 | "iata": "MCI", 2262 | "lat": 39.2975997925, 2263 | "lon": -94.7138977051, 2264 | "country": "美国", 2265 | "cca2": "US", 2266 | "region": "北美洲", 2267 | "city": "堪萨斯城" 2268 | }, 2269 | { 2270 | "iata": "STL", 2271 | "lat": 38.7486991882, 2272 | "lon": -90.3700027466, 2273 | "country": "美国", 2274 | "cca2": "US", 2275 | "region": "北美洲", 2276 | "city": "圣路易斯" 2277 | }, 2278 | { 2279 | "iata": "OMA", 2280 | "lat": 41.3031997681, 2281 | "lon": -95.8940963745, 2282 | "country": "美国", 2283 | "cca2": "US", 2284 | "region": "北美洲", 2285 | "city": "奥马哈" 2286 | }, 2287 | { 2288 | "iata": "LAS", 2289 | "lat": 36.08010101, 2290 | "lon": -115.1520004, 2291 | "country": "美国", 2292 | "cca2": "US", 2293 | "region": "北美洲", 2294 | "city": "拉斯维加斯" 2295 | }, 2296 | { 2297 | "iata": "EWR", 2298 | "lat": 40.6925010681, 2299 | "lon": -74.1687011719, 2300 | "country": "美国", 2301 | "cca2": "US", 2302 | "region": "北美洲", 2303 | "city": "纽瓦克" 2304 | }, 2305 | { 2306 | "iata": "ABQ", 2307 | "lat": 35.0844, 2308 | "lon": -106.6504, 2309 | "country": "美国", 2310 | "cca2": "US", 2311 | "region": "北美洲", 2312 | "city": "阿尔伯克基" 2313 | }, 2314 | { 2315 | "iata": "BUF", 2316 | "lat": 42.94049835, 2317 | "lon": -78.73220062, 2318 | "country": "美国", 2319 | "cca2": "US", 2320 | "region": "北美洲", 2321 | "city": "布法罗" 2322 | }, 2323 | { 2324 | "iata": "CLT", 2325 | "lat": 35.2140007019, 2326 | "lon": -80.9430999756, 2327 | "country": "美国", 2328 | "cca2": "US", 2329 | "region": "北美洲", 2330 | "city": "夏洛特敦" 2331 | }, 2332 | { 2333 | "iata": "CMH", 2334 | "lat": 39.9980010986, 2335 | "lon": -82.8918991089, 2336 | "country": "美国", 2337 | "cca2": "US", 2338 | "region": "北美洲", 2339 | "city": "哥伦布" 2340 | }, 2341 | { 2342 | "iata": "PDX", 2343 | "lat": 45.58869934, 2344 | "lon": -122.5979996, 2345 | "country": "美国", 2346 | "cca2": "US", 2347 | "region": "北美洲", 2348 | "city": "波特兰" 2349 | }, 2350 | { 2351 | "iata": "PHL", 2352 | "lat": 39.8718986511, 2353 | "lon": -75.2410964966, 2354 | "country": "美国", 2355 | "cca2": "US", 2356 | "region": "北美洲", 2357 | "city": "费城" 2358 | }, 2359 | { 2360 | "iata": "PIT", 2361 | "lat": 40.49150085, 2362 | "lon": -80.23290253, 2363 | "country": "美国", 2364 | "cca2": "US", 2365 | "region": "北美洲", 2366 | "city": "匹兹堡" 2367 | }, 2368 | { 2369 | "iata": "FSD", 2370 | "lat": 43.540819819502, 2371 | "lon": -96.65511577730963, 2372 | "country": "美国", 2373 | "cca2": "US", 2374 | "region": "北美洲", 2375 | "city": "苏瀑布" 2376 | }, 2377 | { 2378 | "iata": "MEM", 2379 | "lat": 35.0424003601, 2380 | "lon": -89.9766998291, 2381 | "country": "美国", 2382 | "cca2": "US", 2383 | "region": "北美洲", 2384 | "city": "孟菲斯" 2385 | }, 2386 | { 2387 | "iata": "BNA", 2388 | "lat": 36.1245002747, 2389 | "lon": -86.6781997681, 2390 | "country": "美国", 2391 | "cca2": "US", 2392 | "region": "北美洲", 2393 | "city": "纳什维尔" 2394 | }, 2395 | { 2396 | "iata": "AUS", 2397 | "lat": 30.1975, 2398 | "lon": -97.6664, 2399 | "country": "美国", 2400 | "cca2": "US", 2401 | "region": "北美洲", 2402 | "city": "奥斯汀" 2403 | }, 2404 | { 2405 | "iata": "DFW", 2406 | "lat": 32.8968009949, 2407 | "lon": -97.0380020142, 2408 | "country": "美国", 2409 | "cca2": "US", 2410 | "region": "北美洲", 2411 | "city": "达拉斯" 2412 | }, 2413 | { 2414 | "iata": "IAH", 2415 | "lat": 29.9843997955, 2416 | "lon": -95.3414001465, 2417 | "country": "美国", 2418 | "cca2": "US", 2419 | "region": "北美洲", 2420 | "city": "休斯顿" 2421 | }, 2422 | { 2423 | "iata": "MFE", 2424 | "lat": 26.17580032, 2425 | "lon": -98.23860168, 2426 | "country": "美国", 2427 | "cca2": "US", 2428 | "region": "北美洲", 2429 | "city": "麦卡伦" 2430 | }, 2431 | { 2432 | "iata": "SLC", 2433 | "lat": 40.7883987427, 2434 | "lon": -111.977996826, 2435 | "country": "美国", 2436 | "cca2": "US", 2437 | "region": "北美洲", 2438 | "city": "盐湖城" 2439 | }, 2440 | { 2441 | "iata": "IAD", 2442 | "lat": 38.94449997, 2443 | "lon": -77.45580292, 2444 | "country": "美国", 2445 | "cca2": "US", 2446 | "region": "北美洲", 2447 | "city": "阿什本" 2448 | }, 2449 | { 2450 | "iata": "ORF", 2451 | "lat": 36.8945999146, 2452 | "lon": -76.2012023926, 2453 | "country": "美国", 2454 | "cca2": "US", 2455 | "region": "北美洲", 2456 | "city": "诺福克" 2457 | }, 2458 | { 2459 | "iata": "RIC", 2460 | "lat": 37.5051994324, 2461 | "lon": -77.3197021484, 2462 | "country": "美国", 2463 | "cca2": "US", 2464 | "region": "北美洲", 2465 | "city": "里士满" 2466 | }, 2467 | { 2468 | "iata": "SEA", 2469 | "lat": 47.4490013123, 2470 | "lon": -122.308998108, 2471 | "country": "美国", 2472 | "cca2": "US", 2473 | "region": "北美洲", 2474 | "city": "西雅图" 2475 | }, 2476 | { 2477 | "iata": "TAS", 2478 | "lat": 41.257900238, 2479 | "lon": 69.2811965942, 2480 | "country": "乌兹别克斯坦", 2481 | "cca2": "UZ", 2482 | "region": "亚太", 2483 | "city": "塔什干" 2484 | }, 2485 | { 2486 | "iata": "HAN", 2487 | "lat": 21.221200943, 2488 | "lon": 105.806999206, 2489 | "country": "越南", 2490 | "cca2": "VN", 2491 | "region": "亚太", 2492 | "city": "河内" 2493 | }, 2494 | { 2495 | "iata": "SGN", 2496 | "lat": 10.8187999725, 2497 | "lon": 106.652000427, 2498 | "country": "越南", 2499 | "cca2": "VN", 2500 | "region": "亚太", 2501 | "city": "胡志明市" 2502 | }, 2503 | { 2504 | "iata": "HRE", 2505 | "lat": -17.9318008423, 2506 | "lon": 31.0928001404, 2507 | "country": "津巴布韦", 2508 | "cca2": "ZW", 2509 | "region": "非洲", 2510 | "city": "哈拉雷" 2511 | } 2512 | ] -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "encoding/csv" 6 | "encoding/json" 7 | "flag" 8 | "fmt" 9 | "io" 10 | "net" 11 | "net/http" 12 | "os" 13 | "os/exec" 14 | "regexp" 15 | "runtime" 16 | "sort" 17 | "strconv" 18 | "strings" 19 | "sync" 20 | "sync/atomic" 21 | "time" 22 | ) 23 | 24 | const ( 25 | timeout = 1 * time.Second // 超时时间 26 | maxDuration = 2 * time.Second // 最大持续时间 27 | ) 28 | 29 | var ( 30 | File = flag.String("file", "ip.txt", "IP地址文件名称,格式为 ip port ,就是IP和端口之间用空格隔开") // IP地址文件名称 31 | outFile = flag.String("outfile", "ip.csv", "输出文件名称") // 输出文件名称 32 | maxThreads = flag.Int("max", 100, "并发请求最大协程数") // 最大协程数 33 | speedTest = flag.Int("speedtest", 5, "下载测速协程数量,设为0禁用测速") // 下载测速协程数量 34 | speedLimit = flag.Int("speedlimit", 0, "最低下载速度(MB/s)") // 最低下载速度 35 | speedTestURL = flag.String("url", "speed.cloudflare.com/__down?bytes=500000000", "测速文件地址") // 测速文件地址 36 | enableTLS = flag.Bool("tls", true, "是否启用TLS") // TLS是否启用 37 | TCPurl = flag.String("tcpurl", "www.speedtest.net", "TCP请求地址") // TCP请求地址 38 | delay = flag.Int("delay", 220, "延迟阈值(ms)") // 新增延迟阈值参数 39 | ) 40 | 41 | type result struct { 42 | ip string // IP地址 43 | port int // 端口 44 | dataCenter string // 数据中心 45 | region string // 地区 46 | cca1 string // 国家代码 47 | cca2 string // 国家 48 | city string // 城市 49 | latency string // 延迟 50 | tcpDuration time.Duration // TCP请求延迟 51 | } 52 | 53 | type speedtestresult struct { 54 | result 55 | downloadSpeed float64 // 下载速度 56 | } 57 | 58 | type location struct { 59 | Iata string `json:"iata"` 60 | Lat float64 `json:"lat"` 61 | Lon float64 `json:"lon"` 62 | Cca1 string `json:"cca1"` 63 | Cca2 string `json:"cca2"` 64 | Region string `json:"region"` 65 | City string `json:"city"` 66 | } 67 | 68 | // 尝试提升文件描述符的上限 69 | func increaseMaxOpenFiles() { 70 | fmt.Println("正在尝试提升文件描述符的上限...") 71 | cmd := exec.Command("bash", "-c", "ulimit -n 10000") 72 | _, err := cmd.CombinedOutput() 73 | if err != nil { 74 | fmt.Printf("提升文件描述符上限时出现错误: %v\n", err) 75 | } else { 76 | fmt.Printf("文件描述符上限已提升!\n") 77 | } 78 | } 79 | 80 | func main() { 81 | flag.Parse() 82 | 83 | var validCount int32 // 有效IP计数器 84 | startTime := time.Now() 85 | osType := runtime.GOOS 86 | // 如果是linux系统,尝试提升文件描述符的上限 87 | // 判断是否以root用户运行 88 | 89 | if osType == "linux" && os.Getuid() == 0 { 90 | increaseMaxOpenFiles() 91 | } 92 | 93 | var locations []location 94 | ///////////////////////////// 95 | body := `[{"iata":"TIA","lat":41.4146995544,"lon":19.7206001282,"cca2":"阿尔巴尼亚","cca1":"AL","region":"欧洲","city":"地拉那"},{"iata":"KHN","lat":41.4146995544,"lon":19.7206001282,"cca2":"中国","cca1":"CN","region":"亚洲","city":"南昌"},{"iata":"ALG","lat":36.6910018921,"lon":3.2154099941,"cca2":"阿尔及利亚","cca1":"DZ","region":"非洲","city":"阿尔及尔"},{"iata":"ORN","lat":35.6911,"lon":-0.6416,"cca2":"阿尔及利亚","cca1":"DZ","region":"非洲","city":"奥兰"},{"iata":"LAD","lat":-8.8583698273,"lon":13.2312002182,"cca2":"安哥拉","cca1":"AO","region":"非洲","city":"罗安达"},{"iata":"EZE","lat":-34.8222,"lon":-58.5358,"cca2":"阿根廷","cca1":"AR","region":"南美","city":"布宜诺斯艾利斯"},{"iata":"COR","lat":-31.31,"lon":-64.208333,"cca2":"阿根廷","cca1":"AR","region":"南美","city":"科尔多瓦"},{"iata":"NQN","lat":-38.9490013123,"lon":-68.1557006836,"cca2":"阿根廷","cca1":"AR","region":"南美","city":"内乌肯"},{"iata":"EVN","lat":40.1473007202,"lon":44.3959007263,"cca2":"亚美尼亚","cca1":"AM","region":"中东","city":"埃里温"},{"iata":"ADL","lat":-34.9431729,"lon":138.5335637,"cca2":"澳大利亚","cca1":"AU","region":"大洋洲","city":"阿德莱德"},{"iata":"BNE","lat":-27.3841991425,"lon":153.117004394,"cca2":"澳大利亚","cca1":"AU","region":"大洋洲","city":"布里斯班"},{"iata":"CBR","lat":-35.3069000244,"lon":149.1950073242,"cca2":"澳大利亚","cca1":"AU","region":"大洋洲","city":"堪培拉"},{"iata":"HBA","lat":-42.883209,"lon":147.331665,"cca2":"澳大利亚","cca1":"AU","region":"大洋洲","city":"霍巴特"},{"iata":"MEL","lat":-37.6733016968,"lon":144.843002319,"cca2":"澳大利亚","cca1":"AU","region":"大洋洲","city":"墨尔本"},{"iata":"PER","lat":-31.9402999878,"lon":115.967002869,"cca2":"澳大利亚","cca1":"AU","region":"大洋洲","city":"珀斯"},{"iata":"SYD","lat":-33.9460983276,"lon":151.177001953,"cca2":"澳大利亚","cca1":"AU","region":"大洋洲","city":"悉尼"},{"iata":"VIE","lat":48.1102981567,"lon":16.5697002411,"cca2":"奥地利","cca1":"AT","region":"欧洲","city":"维也纳"},{"iata":"LLK","lat":38.7463989258,"lon":48.8180007935,"cca2":"阿塞拜疆","cca1":"AZ","region":"中东","city":"Astara"},{"iata":"GYD","lat":40.4674987793,"lon":50.0466995239,"cca2":"阿塞拜疆","cca1":"AZ","region":"中东","city":"巴库"},{"iata":"BAH","lat":26.2707996368,"lon":50.6335983276,"cca2":"巴林","cca1":"BH","region":"中东","city":"麦纳麦"},{"iata":"CGP","lat":22.2495995,"lon":91.8133011,"cca2":"孟加拉国","cca1":"BD","region":"亚太","city":"吉大港"},{"iata":"DAC","lat":23.843347,"lon":90.397783,"cca2":"孟加拉国","cca1":"BD","region":"亚太","city":"达卡"},{"iata":"JSR","lat":23.1837997437,"lon":89.1607971191,"cca2":"孟加拉国","cca1":"BD","region":"亚太","city":"杰索尔"},{"iata":"MSQ","lat":53.9006,"lon":27.599,"cca2":"白俄罗斯","cca1":"BY","region":"欧洲","city":"明斯克"},{"iata":"BRU","lat":50.9014015198,"lon":4.4844398499,"cca2":"比利时","cca1":"BE","region":"欧洲","city":"布鲁塞尔"},{"iata":"PBH","lat":27.4712,"lon":89.6339,"cca2":"不丹","cca1":"BT","region":"亚太","city":"廷布"},{"iata":"GBE","lat":-24.6282,"lon":25.9231,"cca2":"不丹","cca1":"BW","region":"非洲","city":"博茨瓦纳"},{"iata":"QWJ","lat":-22.738,"lon":-47.334,"cca2":"巴西","cca1":"BR","region":"南美","city":"亚美利加纳"},{"iata":"BEL","lat":-1.4563,"lon":-48.5013,"cca2":"巴西","cca1":"BR","region":"南美","city":"贝伦"},{"iata":"CNF","lat":-19.624444,"lon":-43.971944,"cca2":"巴西","cca1":"BR","region":"南美","city":"贝洛奥里宗特"},{"iata":"BNU","lat":-26.89245,"lon":-49.07696,"cca2":"巴西","cca1":"BR","region":"南美","city":"布鲁梅瑙"},{"iata":"BSB","lat":-15.79824,"lon":-47.90859,"cca2":"巴西","cca1":"BR","region":"南美","city":"利亚"},{"iata":"CFC","lat":-26.7762,"lon":-51.0125,"cca2":"巴西","cca1":"BR","region":"南美","city":"卡萨多尔"},{"iata":"VCP","lat":-22.90662,"lon":-47.08576,"cca2":"巴西","cca1":"BR","region":"南美","city":"坎皮纳斯"},{"iata":"CAW","lat":-21.698299408,"lon":-41.301700592,"cca2":"巴西","cca1":"BR","region":"南美","city":"坎普斯"},{"iata":"CGB","lat":-15.59611,"lon":-56.09667,"cca2":"巴西","cca1":"BR","region":"南美","city":"库亚巴"},{"iata":"CWB","lat":-25.5284996033,"lon":-49.1758003235,"cca2":"巴西","cca1":"BR","region":"南美","city":"库里蒂巴"},{"iata":"FLN","lat":-27.6702785492,"lon":-48.5525016785,"cca2":"巴西","cca1":"BR","region":"南美","city":"弗洛里亚诺波利斯"},{"iata":"FOR","lat":-3.7762799263,"lon":-38.5326004028,"cca2":"巴西","cca1":"BR","region":"南美","city":"福塔莱萨"},{"iata":"GYN","lat":-16.69727,"lon":-49.26851,"cca2":"巴西","cca1":"BR","region":"南美","city":"戈亚尼亚"},{"iata":"ITJ","lat":-27.6116676331,"lon":-48.6727790833,"cca2":"巴西","cca1":"BR","region":"南美","city":"伊塔雅伊"},{"iata":"JOI","lat":-26.304408,"lon":-48.846383,"cca2":"巴西","cca1":"BR","region":"南美","city":"若茵维莱"},{"iata":"JDO","lat":-7.2242,"lon":-39.313,"cca2":"巴西","cca1":"BR","region":"南美","city":"北茹阿泽鲁"},{"iata":"MAO","lat":-3.11286,"lon":-60.01949,"cca2":"巴西","cca1":"BR","region":"南美","city":"马瑙斯"},{"iata":"POA","lat":-29.9944000244,"lon":-51.1713981628,"cca2":"巴西","cca1":"BR","region":"南美","city":"阿雷格里港"},{"iata":"REC","lat":-8.1264896393,"lon":-34.9235992432,"cca2":"巴西","cca1":"BR","region":"南美","city":"累西腓"},{"iata":"RAO","lat":-21.1363887787,"lon":-47.7766685486,"cca2":"巴西","cca1":"BR","region":"南美","city":"里贝朗普雷图"},{"iata":"GIG","lat":-22.8099994659,"lon":-43.2505569458,"cca2":"巴西","cca1":"BR","region":"南美","city":"里约热内卢"},{"iata":"SSA","lat":-12.9086112976,"lon":-38.3224983215,"cca2":"巴西","cca1":"BR","region":"南美","city":"萨尔瓦多"},{"iata":"SJP","lat":-20.807157,"lon":-49.378994,"cca2":"巴西","cca1":"BR","region":"南美","city":"圣若泽"},{"iata":"SJK","lat":-23.1791,"lon":-45.8872,"cca2":"巴西","cca1":"BR","region":"南美","city":"圣若泽杜斯坎普斯"},{"iata":"GRU","lat":-23.4355564117,"lon":-46.4730567932,"cca2":"巴西","cca1":"BR","region":"南美","city":"圣保罗"},{"iata":"SOD","lat":-23.54389,"lon":-46.63445,"cca2":"巴西","cca1":"BR","region":"南美","city":"索罗卡巴"},{"iata":"NVT","lat":-26.8251,"lon":-49.2695,"cca2":"巴西","cca1":"BR","region":"南美","city":"圣卡塔琳娜"},{"iata":"UDI","lat":-18.8836116791,"lon":-48.225276947,"cca2":"巴西","cca1":"BR","region":"南美","city":"乌贝兰迪亚"},{"iata":"VIX","lat":-20.64871,"lon":-41.90857,"cca2":"巴西","cca1":"BR","region":"南美","city":"维多利亚"},{"iata":"BWN","lat":4.903052,"lon":114.939819,"cca2":"文莱","cca1":"BN","region":"亚太","city":"斯里巴加湾"},{"iata":"SOF","lat":42.6966934204,"lon":23.4114360809,"cca2":"保加利亚","cca1":"BG","region":"欧洲","city":"索菲亚"},{"iata":"OUA","lat":12.3531999588,"lon":-1.5124200583,"cca2":"布基纳法索","cca1":"BF","region":"非洲","city":"瓦加杜古"},{"iata":"PNH","lat":11.5466003418,"lon":104.84400177,"cca2":"柬埔寨","cca1":"KH","region":"亚太","city":"金边"},{"iata":"YYC","lat":51.113899231,"lon":-114.019996643,"cca2":"加拿大","cca1":"CA","region":"北美洲","city":"卡尔加里"},{"iata":"YVR","lat":49.193901062,"lon":-123.183998108,"cca2":"加拿大","cca1":"CA","region":"北美洲","city":"温哥华"},{"iata":"YWG","lat":49.9099998474,"lon":-97.2398986816,"cca2":"加拿大","cca1":"CA","region":"北美洲","city":"温尼伯"},{"iata":"YOW","lat":45.3224983215,"lon":-75.6691970825,"cca2":"加拿大","cca1":"CA","region":"北美洲","city":"渥太华"},{"iata":"YYZ","lat":43.6772003174,"lon":-79.6305999756,"cca2":"加拿大","cca1":"CA","region":"北美洲","city":"多伦多"},{"iata":"YUL","lat":45.4706001282,"lon":-73.7407989502,"cca2":"加拿大","cca1":"CA","region":"北美洲","city":"蒙特利尔"},{"iata":"YXE","lat":52.1707992554,"lon":-106.699996948,"cca2":"加拿大","cca1":"CA","region":"北美洲","city":"萨斯卡通"},{"iata":"ARI","lat":-18.348611,"lon":-70.338889,"cca2":"智利","cca1":"CL","region":"南美","city":"阿里卡"},{"iata":"CCP","lat":-36.8201,"lon":-73.0444,"cca2":"智利","cca1":"CL","region":"南美","city":"康塞普西翁"},{"iata":"SCL","lat":-33.3930015564,"lon":-70.7857971191,"cca2":"智利","cca1":"CL","region":"南美","city":"圣地亚哥"},{"iata":"BOG","lat":4.70159,"lon":-74.1469,"cca2":"哥伦比亚","cca1":"CO","region":"南美","city":"波哥大"},{"iata":"MDE","lat":6.16454,"lon":-75.4231,"cca2":"哥伦比亚","cca1":"CO","region":"南美","city":"麦德林"},{"iata":"FIH","lat":-4.3857498169,"lon":15.4446001053,"cca2":"刚果","cca1":"CD","region":"非洲","city":"金沙萨"},{"iata":"SJO","lat":9.9938602448,"lon":-84.2088012695,"cca2":"哥斯达黎加","cca1":"CR","region":"南美","city":"圣何塞"},{"iata":"ZAG","lat":45.7429008484,"lon":16.0687999725,"cca2":"克罗地亚","cca1":"HR","region":"欧洲","city":"萨格勒布"},{"iata":"CUR","lat":12.1888999939,"lon":-68.9598007202,"cca2":"库拉索岛","cca1":"CW","region":"南美","city":"库拉索岛"},{"iata":"LCA","lat":34.8750991821,"lon":33.6249008179,"cca2":"塞浦路斯","cca1":"CY","region":"欧洲","city":"尼科西亚"},{"iata":"PRG","lat":50.1007995605,"lon":14.2600002289,"cca2":"捷克","cca1":"CZ","region":"欧洲","city":"布拉格"},{"iata":"CPH","lat":55.6179008484,"lon":12.6560001373,"cca2":"丹麦","cca1":"DK","region":"欧洲","city":"哥本哈根"},{"iata":"JIB","lat":11.5473003387,"lon":43.1595001221,"cca2":"吉布提","cca1":"DJ","region":"非洲","city":"吉布提"},{"iata":"SDQ","lat":18.4297008514,"lon":-69.6688995361,"cca2":"多米尼加","cca1":"DO","region":"北美洲","city":"圣多明各"},{"iata":"GYE","lat":-2.1894,"lon":-79.8891,"cca2":"厄瓜多尔","cca1":"EC","region":"南美","city":"瓜亚基尔"},{"iata":"UIO","lat":-0.1291666667,"lon":-78.3575,"cca2":"厄瓜多尔","cca1":"EC","region":"南美","city":"基多"},{"iata":"TLL","lat":59.4132995605,"lon":24.8327999115,"cca2":"爱沙尼亚","cca1":"EE","region":"欧洲","city":"塔林"},{"iata":"HEL","lat":60.317199707,"lon":24.963300705,"cca2":"芬兰","cca1":"FI","region":"欧洲","city":"赫尔辛基"},{"iata":"LYS","lat":45.7263,"lon":5.0908,"cca2":"法国","cca1":"FR","region":"欧洲","city":"里昂"},{"iata":"MRS","lat":43.439271922,"lon":5.2214241028,"cca2":"法国","cca1":"FR","region":"欧洲","city":"马赛"},{"iata":"CDG","lat":49.0127983093,"lon":2.5499999523,"cca2":"法国","cca1":"FR","region":"欧洲","city":"巴黎"},{"iata":"PPT","lat":-17.5536994934,"lon":-149.606994629,"cca2":"玻利尼西亚","cca1":"PF","region":"大洋洲","city":"塔希提岛"},{"iata":"TBS","lat":41.6692008972,"lon":44.95470047,"cca2":"格鲁吉亚","cca1":"GE","region":"欧洲","city":"第比利斯"},{"iata":"TXL","lat":52.5597000122,"lon":13.2876996994,"cca2":"德国","cca1":"DE","region":"欧洲","city":"柏林"},{"iata":"DUS","lat":51.2895011902,"lon":6.7667798996,"cca2":"德国","cca1":"DE","region":"欧洲","city":"杜塞尔多夫"},{"iata":"FRA","lat":50.0264015198,"lon":8.543129921,"cca2":"德国","cca1":"DE","region":"欧洲","city":"法兰克福"},{"iata":"HAM","lat":53.6304016113,"lon":9.9882297516,"cca2":"德国","cca1":"DE","region":"欧洲","city":"汉堡"},{"iata":"MUC","lat":48.3538017273,"lon":11.7861003876,"cca2":"德国","cca1":"DE","region":"欧洲","city":"慕尼黑"},{"iata":"STR","lat":48.783333,"lon":9.183333,"cca2":"德国","cca1":"DE","region":"欧洲","city":"斯图加特"},{"iata":"ACC","lat":5.614818,"lon":-0.205874,"cca2":"加纳","cca1":"GH","region":"非洲","city":"阿克拉"},{"iata":"ATH","lat":37.9364013672,"lon":23.9444999695,"cca2":"希腊","cca1":"GR","region":"欧洲","city":"雅典"},{"iata":"SKG","lat":40.5196990967,"lon":22.9708995819,"cca2":"希腊","cca1":"GR","region":"欧洲","city":"塞萨洛尼基"},{"iata":"GND","lat":12.007116,"lon":-61.7882288,"cca2":"格林纳达","cca1":"GD","region":"南美","city":"圣乔治"},{"iata":"GUM","lat":13.4834003448,"lon":144.796005249,"cca2":"关岛","cca1":"GU","region":"亚太","city":"阿加尼亚"},{"iata":"GUA","lat":14.5832996368,"lon":-90.5274963379,"cca2":"危地马拉","cca1":"GT","region":"北美洲","city":"危地马拉"},{"iata":"GEO","lat":6.825648,"lon":-58.163756,"cca2":"圭亚那","cca1":"GY","region":"南美","city":"乔治城"},{"iata":"PAP","lat":18.5799999237,"lon":-72.2925033569,"cca2":"海地","cca1":"HT","region":"北美洲","city":"太子港"},{"iata":"TGU","lat":14.0608,"lon":-87.2172,"cca2":"洪都拉斯","cca1":"HN","region":"南美","city":"洪都拉斯"},{"iata":"HKG","lat":22.3089008331,"lon":113.915000916,"cca2":"香港","cca1":"HK","region":"亚太","city":"香港"},{"iata":"BUD","lat":47.4369010925,"lon":19.2555999756,"cca2":"匈牙利","cca1":"HU","region":"欧洲","city":"布达佩斯"},{"iata":"KEF","lat":63.9850006104,"lon":-22.6056003571,"cca2":"冰岛","cca1":"IS","region":"欧洲","city":"雷克雅未克"},{"iata":"AMD","lat":23.0225,"lon":72.5714,"cca2":"印度","cca1":"IN","region":"亚太","city":"艾哈迈达巴德"},{"iata":"BLR","lat":13.7835719,"lon":76.6165937,"cca2":"印度","cca1":"IN","region":"亚太","city":"班加罗尔"},{"iata":"BBI","lat":20.2961,"lon":85.8245,"cca2":"印度","cca1":"IN","region":"亚太","city":"布巴内斯瓦尔"},{"iata":"IXC","lat":30.673500061,"lon":76.7884979248,"cca2":"印度","region":"亚太","city":"昌迪加尔"},{"iata":"MAA","lat":12.9900054932,"lon":80.1692962646,"cca2":"印度","cca1":"IN","region":"亚太","city":"金奈"},{"iata":"HYD","lat":17.2313175201,"lon":78.4298553467,"cca2":"印度","cca1":"IN","region":"亚太","city":"海得拉巴"},{"iata":"CNN","lat":11.915858,"lon":75.55094,"cca2":"印度","cca1":"IN","region":"亚太","city":"坎纳诺尔"},{"iata":"KNU","lat":26.4499,"lon":80.3319,"cca2":"印度","cca1":"IN","region":"亚太","city":"坎普尔"},{"iata":"COK","lat":9.9312,"lon":76.2673,"cca2":"印度","cca1":"IN","region":"亚太","city":"高知城"},{"iata":"CCU","lat":22.6476933,"lon":88.4349249,"cca2":"印度","cca1":"IN","region":"亚太","city":"加尔各答"},{"iata":"BOM","lat":19.0886993408,"lon":72.8678970337,"cca2":"印度","cca1":"IN","region":"亚太","city":"孟买"},{"iata":"NAG","lat":21.1610714,"lon":79.0024702,"cca2":"印度","cca1":"IN","region":"亚太","city":"那格浦尔"},{"iata":"DEL","lat":28.5664997101,"lon":77.1031036377,"cca2":"印度","cca1":"IN","region":"亚太","city":"新德里"},{"iata":"PAT","lat":25.591299057,"lon":85.0879974365,"cca2":"印度","cca1":"IN","region":"亚太","city":"巴特那"},{"iata":"DPS","lat":-8.748169899,"lon":115.1669998169,"cca2":"印尼","cca1":"ID","region":"亚太","city":"登巴萨"},{"iata":"CGK","lat":-6.1275229,"lon":106.6515118,"cca2":"印尼","cca1":"ID","region":"亚太","city":"雅加达"},{"iata":"JOG","lat":-7.7881798744,"lon":110.4319992065,"cca2":"印尼","cca1":"ID","region":"亚太","city":"日惹特区"},{"iata":"BGW","lat":33.2625007629,"lon":44.2346000671,"cca2":"伊拉克","cca1":"IQ","region":"中东","city":"巴格达"},{"iata":"BSR","lat":30.5491008759,"lon":47.6621017456,"cca2":"伊拉克","cca1":"IQ","region":"中东","city":"巴士拉"},{"iata":"EBL","lat":36.1901,"lon":43.993,"cca2":"伊拉克","cca1":"IQ","region":"中东","city":"阿尔比尔"},{"iata":"NJF","lat":31.989722,"lon":44.404167,"cca2":"伊拉克","cca1":"IQ","region":"中东","city":"纳杰夫"},{"iata":"XNH","lat":30.9358005524,"lon":46.0900993347,"cca2":"伊拉克","cca1":"IQ","region":"中东","city":"纳西里耶"},{"iata":"ISU","lat":35.5668,"lon":45.4161,"cca2":"伊拉克","cca1":"IQ","region":"中东","city":"苏莱曼尼亚"},{"iata":"ORK","lat":51.8413009644,"lon":-8.491109848,"cca2":"爱尔兰","cca1":"IE","region":"欧洲","city":"科克"},{"iata":"DUB","lat":53.4212989807,"lon":-6.270070076,"cca2":"爱尔兰","cca1":"IE","region":"欧洲","city":"都柏林"},{"iata":"HFA","lat":32.78492,"lon":34.96069,"cca2":"以色列","cca1":"IL","region":"中东","city":"海法"},{"iata":"TLV","lat":32.0113983154,"lon":34.8866996765,"cca2":"以色列","cca1":"IL","region":"中东","city":"特拉维夫"},{"iata":"MXP","lat":45.6305999756,"lon":8.7281103134,"cca2":"意大利","cca1":"IT","region":"欧洲","city":"米兰"},{"iata":"PMO","lat":38.16114,"lon":13.31546,"cca2":"意大利","cca1":"IT","region":"欧洲","city":"巴勒莫"},{"iata":"FCO","lat":41.8045005798,"lon":12.2508001328,"cca2":"意大利","cca1":"IT","region":"欧洲","city":"罗马"},{"iata":"KIN","lat":17.9951,"lon":-76.7846,"cca2":"牙买加","cca1":"JM","region":"北美洲","city":"金斯顿"},{"iata":"FUK","lat":33.5902,"lon":130.4017,"cca2":"日本","cca1":"JP","region":"亚太","city":"福冈"},{"iata":"OKA","lat":26.1958,"lon":127.646,"cca2":"日本","cca1":"JP","region":"亚太","city":"那霸"},{"iata":"KIX","lat":34.4272994995,"lon":135.244003296,"cca2":"日本","cca1":"JP","region":"亚太","city":"大阪"},{"iata":"NRT","lat":35.7647018433,"lon":140.386001587,"cca2":"日本","cca1":"JP","region":"亚太","city":"东京"},{"iata":"AMM","lat":31.7226009369,"lon":35.9931983948,"cca2":"约旦","cca1":"JO","region":"中东","city":"安曼"},{"iata":"ALA","lat":43.3521003723,"lon":77.0404968262,"cca2":"哈萨克斯坦","cca1":"KZ","region":"亚太","city":"阿拉木图"},{"iata":"MBA","lat":-4.0348300934,"lon":39.5942001343,"cca2":"肯尼亚","cca1":"KE","region":"非洲","city":"蒙巴萨"},{"iata":"NBO","lat":-1.319239974,"lon":36.9277992249,"cca2":"肯尼亚","cca1":"KE","region":"非洲","city":"内罗毕"},{"iata":"ICN","lat":37.4691009521,"lon":126.450996399,"cca2":"韩国","cca1":"KR","region":"亚太","city":"首尔"},{"iata":"KWI","lat":29.226600647,"lon":47.9688987732,"cca1":"科威特","cca1":"KW","region":"中东","city":"科威特城"},{"iata":"VTE","lat":17.9757,"lon":102.5683,"cca2":"老挝","cca1":"LA","region":"亚太","city":"万象"},{"iata":"RIX","lat":56.9235992432,"lon":23.9710998535,"cca2":"拉脱维亚","cca1":"LV","region":"欧洲","city":"里加"},{"iata":"BEY","lat":33.8208999634,"lon":35.4883995056,"cca2":"黎巴嫩","cca1":"LB","region":"中东","city":"贝鲁特"},{"iata":"VNO","lat":54.6341018677,"lon":25.2858009338,"cca2":"立陶宛","cca1":"LT","region":"欧洲","city":"维尔纽斯"},{"iata":"LUX","lat":49.6265983582,"lon":6.211520195,"cca2":"卢森堡","cca1":"LU","region":"欧洲","city":"卢森堡"},{"iata":"MFM","lat":22.1495990753,"lon":113.592002869,"cca2":"澳门","cca1":"MO","region":"亚太","city":"澳门"},{"iata":"TNR","lat":-18.91368,"lon":47.53613,"cca2":"马达加斯加","cca1":"MG","region":"非洲","city":"塔那那利佛"},{"iata":"JHB","lat":1.635848,"lon":103.665943,"cca2":"马来西亚","cca1":"MY","region":"亚太","city":"柔佛州"},{"iata":"KUL","lat":2.745579958,"lon":101.709999084,"cca2":"马来西亚","cca1":"MY","region":"亚太","city":"吉隆坡"},{"iata":"MLE","lat":4.1748,"lon":73.50888,"cca2":"马尔代夫","cca1":"MV","region":"亚太","city":"马累"},{"iata":"MRU","lat":-20.4302005768,"lon":57.6836013794,"cca2":"毛里求斯","cca1":"MU","region":"非洲","city":"路易港"},{"iata":"GDL","lat":20.5217990875,"lon":-103.3109970093,"cca2":"墨西哥","cca1":"MX","region":"北美洲","city":"瓜达拉哈拉"},{"iata":"MEX","lat":19.4363002777,"lon":-99.0720977783,"cca2":"墨西哥","cca1":"MX","region":"北美洲","city":"墨西哥"},{"iata":"QRO","lat":20.6173000336,"lon":-100.185997009,"cca2":"墨西哥","cca1":"MX","region":"北美洲","city":"克雷塔羅"},{"iata":"KIV","lat":46.9277000427,"lon":28.9309997559,"cca2":"摩尔多瓦","cca1":"MD","region":"欧洲","city":"基希讷乌"},{"iata":"ULN","lat":47.8431015015,"lon":106.766998291,"cca2":"蒙古","cca1":"MN","region":"亚太","city":"蒙古"},{"iata":"CMN","lat":33.3675003052,"lon":-7.5899701118,"cca2":"摩洛哥","cca1":"MA","region":"非洲","city":"卡萨布兰卡"},{"iata":"MPM","lat":-25.9207992554,"lon":32.5726013184,"cca2":"莫桑比克","cca1":"MZ","region":"非洲","city":"马普托"},{"iata":"MDL","lat":21.7051697,"lon":95.9695206,"cca2":"缅甸","cca1":"MM","region":"亚太","city":"曼德勒"},{"iata":"RGN","lat":16.9073009491,"lon":96.1332015991,"cca2":"缅甸","cca1":"MM","region":"亚太","city":"仰光"},{"iata":"KTM","lat":27.6965999603,"lon":85.3591003418,"cca2":"尼泊尔","cca1":"NP","region":"亚太","city":"加德满都"},{"iata":"AMS","lat":52.3086013794,"lon":4.7638897896,"cca2":"荷兰","cca1":"NL","region":"欧洲","city":"阿姆斯特丹"},{"iata":"NOU","lat":-22.0146007538,"lon":166.212997436,"cca2":"新喀里多尼亚","cca1":"NC","region":"大洋洲","city":"努美阿"},{"iata":"AKL","lat":-37.0080986023,"lon":174.792007446,"cca2":"新西兰","cca1":"NZ","region":"大洋洲","city":"奥克兰"},{"iata":"CHC","lat":-43.4893989563,"lon":172.5319976807,"cca2":"新西兰","cca1":"NZ","region":"大洋洲","city":"克赖斯特彻"},{"iata":"LOS","lat":6.5773701668,"lon":3.321160078,"cca2":"尼日利亚","cca1":"NG","region":"非洲","city":"拉各斯"},{"iata":"OSL","lat":60.193901062,"lon":11.100399971,"cca2":"挪威","cca1":"NO","region":"欧洲","city":"奥斯陆"},{"iata":"MCT","lat":23.5932998657,"lon":58.2844009399,"cca2":"阿曼","cca1":"OM","region":"中东","city":"马斯喀特"},{"iata":"ISB","lat":33.6166992188,"lon":73.0991973877,"cca2":"巴基斯坦","cca1":"PK","region":"亚太","city":"伊斯兰堡"},{"iata":"KHI","lat":24.9064998627,"lon":67.1607971191,"cca2":"巴基斯坦","cca1":"PK","region":"亚太","city":"卡拉奇"},{"iata":"LHE","lat":31.5216007233,"lon":74.4036026001,"cca2":"巴基斯坦","cca1":"PK","region":"亚太","city":"拉合尔"},{"iata":"ZDM","lat":32.2719,"lon":35.0194,"cca2":"巴勒斯坦","cca1":"PS","region":"中东","city":"拉姆安拉"},{"iata":"PTY","lat":9.0713596344,"lon":-79.3834991455,"cca2":"巴拿马","cca1":"PA","region":"南美","city":"巴拿马城"},{"iata":"ASU","lat":-25.2399997711,"lon":-57.5200004578,"cca2":"巴拉圭","cca1":"PY","region":"南美","city":"亚松森"},{"iata":"LIM","lat":-12.021900177,"lon":-77.1143035889,"cca2":"秘鲁","cca1":"PE","region":"南美","city":"利马"},{"iata":"CGY","lat":8.4156198502,"lon":124.611000061,"cca2":"菲律宾","cca1":"PH","region":"亚太","city":"哥打巴托市"},{"iata":"CEB","lat":10.3074998856,"lon":123.978996277,"cca2":"菲律宾","cca1":"PH","region":"亚太","city":"宿务"},{"iata":"MNL","lat":14.508600235,"lon":121.019996643,"cca2":"菲律宾","cca1":"PH","region":"亚太","city":"马尼拉"},{"iata":"WAW","lat":52.1656990051,"lon":20.9671001434,"cca2":"波兰","cca1":"PL","region":"欧洲","city":"华沙"},{"iata":"LIS","lat":38.7812995911,"lon":-9.1359195709,"cca2":"葡萄牙","cca1":"PT","region":"欧洲","city":"里斯本"},{"iata":"DOH","lat":25.2605946,"lon":51.6137665,"cca2":"卡塔尔","cca1":"QA","region":"中东","city":"多哈"},{"iata":"RUN","lat":-20.8871002197,"lon":55.5102996826,"cca2":"留尼汪","cca1":"RE","region":"非洲","city":"圣但尼"},{"iata":"OTP","lat":44.5722007751,"lon":26.1021995544,"cca2":"罗马尼亚","cca1":"RO","region":"欧洲","city":"布加勒斯特"},{"iata":"KHV","lat":48.5279998779,"lon":135.18800354,"cca2":"俄罗斯","cca1":"RU","region":"亚太","city":"哈巴罗夫斯克"},{"iata":"KJA","lat":56.0153,"lon":92.8932,"cca2":"俄罗斯","cca1":"RU","region":"亚太","city":"克拉斯诺亚尔斯克"},{"iata":"DME","lat":55.4087982178,"lon":37.9062995911,"cca2":"俄罗斯","cca1":"RU","region":"欧洲","city":"莫斯科"},{"iata":"LED","lat":59.8003005981,"lon":30.2625007629,"cca2":"俄罗斯","cca1":"RU","region":"欧洲","city":"圣彼得堡"},{"iata":"KLD","lat":56.8587,"lon":35.9176,"cca2":"俄罗斯","cca1":"RU","region":"欧洲","city":"特维尔"},{"iata":"SVX","lat":56.8431,"lon":60.6454,"cca2":"俄罗斯","cca1":"RU","region":"亚太","city":"叶卡捷琳堡"},{"iata":"KGL","lat":-1.9686299563,"lon":30.1394996643,"cca2":"卢旺达","cca1":"RW","region":"非洲","city":"基加利"},{"iata":"DMM","lat":26.471200943,"lon":49.7979011536,"cca2":"沙特阿拉伯","cca1":"SA","region":"中东","city":"达曼"},{"iata":"JED","lat":21.679599762,"lon":39.15650177,"cca2":"沙特阿拉伯","cca1":"SA","region":"中东","city":"吉达"},{"iata":"RUH","lat":24.9575996399,"lon":46.6987991333,"cca2":"沙特阿拉伯","cca1":"SA","region":"中东","city":"利雅得"},{"iata":"DKR","lat":14.7412099,"lon":-17.4889771,"cca2":"塞内加尔","cca1":"SN","region":"非洲","city":"达喀尔"},{"iata":"BEG","lat":44.8184013367,"lon":20.3090991974,"cca2":"塞尔维亚","cca1":"RS","region":"欧洲","city":"贝尔格莱德"},{"iata":"SIN","lat":1.3501900434,"lon":103.994003296,"cca2":"新加坡","cca1":"SG","region":"亚太","city":"新加坡"},{"iata":"BTS","lat":48.1486,"lon":17.1077,"cca2":"斯洛伐克","cca1":"SK","region":"欧洲","city":"布拉迪斯拉发"},{"iata":"CPT","lat":-33.9648017883,"lon":18.6016998291,"cca2":"南非","cca1":"ZA","region":"非洲","city":"开普敦"},{"iata":"DUR","lat":-29.6144444444,"lon":31.1197222222,"cca2":"南非","cca1":"ZA","region":"非洲","city":"德班"},{"iata":"JNB","lat":-26.133333,"lon":28.25,"cca2":"南非","cca1":"ZA","region":"非洲","city":"约翰内斯堡"},{"iata":"BCN","lat":41.2971000671,"lon":2.0784599781,"cca2":"西班牙","cca1":"ES","region":"欧洲","city":"巴塞罗那"},{"iata":"MAD","lat":40.4936,"lon":-3.56676,"cca2":"西班牙","cca1":"ES","region":"欧洲","city":"马德里"},{"iata":"CMB","lat":7.1807599068,"lon":79.8841018677,"cca2":"斯里兰卡","cca1":"LK","region":"亚太","city":"科伦坡"},{"iata":"PBM","lat":5.452831,"lon":-55.187783,"cca2":"苏里南","cca1":"SR","region":"南美","city":"帕拉马里博"},{"iata":"GOT","lat":57.6627998352,"lon":12.279800415,"cca2":"瑞典","cca1":"SE","region":"欧洲","city":"哥德堡"},{"iata":"ARN","lat":59.6519012451,"lon":17.9186000824,"cca2":"瑞典","cca1":"SE","region":"欧洲","city":"斯德哥尔摩"},{"iata":"GVA","lat":46.2380981445,"lon":6.1089501381,"cca2":"瑞士","cca1":"CH","region":"欧洲","city":"日内瓦"},{"iata":"ZRH","lat":47.4646987915,"lon":8.5491695404,"cca2":"瑞士","cca1":"CH","region":"欧洲","city":"苏黎世"},{"iata":"KHH","lat":22.5771007538,"lon":120.3499984741,"cca2":"台湾","cca1":"TW","region":"亚太","city":"高雄"},{"iata":"TPE","lat":25.0776996613,"lon":121.233001709,"cca2":"台湾","cca1":"TW","region":"亚太","city":"台北"},{"iata":"DAR","lat":-6.8781099319,"lon":39.2025985718,"cca2":"坦桑尼亚","cca1":"TZ","region":"非洲","city":"达累斯萨拉姆"},{"iata":"BKK","lat":13.6810998917,"lon":100.747001648,"cca2":"泰国","cca1":"TH","region":"亚太","city":"曼谷"},{"iata":"CNX","lat":18.7667999268,"lon":98.962600708,"cca2":"泰国","cca1":"TH","region":"亚太","city":"清迈"},{"iata":"URT","lat":9.1325998306,"lon":99.135597229,"cca2":"泰国","cca1":"TH","region":"亚太","city":"素叻府"},{"iata":"TUN","lat":36.8510017395,"lon":10.2271995544,"cca2":"突尼斯","cca1":"TN","region":"非洲","city":"突尼斯"},{"iata":"IST","lat":40.9768981934,"lon":28.8145999908,"cca2":"土耳其","cca1":"TR","region":"欧洲","city":"伊斯坦布尔"},{"iata":"ADB","lat":38.32377,"lon":27.14317,"cca2":"土耳其","cca1":"TR","region":"欧洲","city":"伊兹密尔"},{"iata":"KBP","lat":50.3450012207,"lon":30.8946990967,"cca2":"乌克兰","cca1":"UA","region":"欧洲","city":"基辅"},{"iata":"DXB","lat":25.2527999878,"lon":55.3643989563,"cca2":"阿联酋","cca1":"AE","region":"中东","city":"迪拜"},{"iata":"EDI","lat":55.9500007629,"lon":-3.3724999428,"cca2":"英国","cca1":"GB","region":"欧洲","city":"爱丁堡"},{"iata":"LHR","lat":51.4706001282,"lon":-0.4619410038,"cca2":"英国","cca1":"GB","region":"欧洲","city":"伦敦"},{"iata":"MAN","lat":53.3536987305,"lon":-2.2749500275,"cca2":"英国","cca1":"GB","region":"欧洲","city":"Manchester"},{"iata":"MGM","lat":32.30059814,"lon":-86.39399719,"cca2":"美国","cca1":"US","region":"北美洲","city":"蒙哥马利"},{"iata":"PHX","lat":33.434299469,"lon":-112.012001038,"cca2":"美国","cca1":"US","region":"北美洲","city":"凤凰城"},{"iata":"LAX","lat":33.94250107,"lon":-118.4079971,"cca2":"美国","cca1":"US","region":"北美洲","city":"洛杉矶"},{"iata":"SMF","lat":38.695400238,"lon":-121.591003418,"cca2":"美国","cca1":"US","region":"北美洲","city":"萨克拉门托"},{"iata":"SAN","lat":32.7336006165,"lon":-117.190002441,"cca2":"美国","cca1":"US","region":"北美洲","city":"圣地亚哥"},{"iata":"SFO","lat":37.6189994812,"lon":-122.375,"cca2":"美国","cca1":"US","region":"北美洲","city":"旧金山"},{"iata":"SJC","lat":37.3625984192,"lon":-121.929000855,"cca2":"美国","cca1":"US","region":"北美洲","city":"圣何塞"},{"iata":"DEN","lat":39.8616981506,"lon":-104.672996521,"cca2":"美国","cca1":"US","region":"北美洲","city":"丹佛"},{"iata":"JAX","lat":30.4941005707,"lon":-81.6878967285,"cca2":"美国","cca1":"US","region":"北美洲","city":"杰克逊维尔"},{"iata":"MIA","lat":25.7931995392,"lon":-80.2906036377,"cca2":"美国","cca1":"US","region":"北美洲","city":"迈阿密"},{"iata":"TLH","lat":30.3964996338,"lon":-84.3503036499,"cca2":"美国","cca1":"US","region":"北美洲","city":"塔拉哈西"},{"iata":"TPA","lat":27.9755001068,"lon":-82.533203125,"cca2":"美国","cca1":"US","region":"北美洲","city":"坦帕市"},{"iata":"ATL","lat":33.6366996765,"lon":-84.4281005859,"cca2":"美国","cca1":"US","region":"北美洲","city":"亚特兰大"},{"iata":"HNL","lat":21.3187007904,"lon":-157.9219970703,"cca2":"美国","cca1":"US","region":"北美洲","city":"檀香山"},{"iata":"ORD","lat":41.97859955,"lon":-87.90480042,"cca2":"美国","cca1":"US","region":"北美洲","city":"芝加哥"},{"iata":"IND","lat":39.717300415,"lon":-86.2944030762,"cca2":"美国","cca1":"US","region":"北美洲","city":"印第安纳波利斯"},{"iata":"BGR","lat":44.8081,"lon":-68.795,"cca2":"美国","cca1":"US","region":"北美洲","city":"班格尔"},{"iata":"BOS","lat":42.36429977,"lon":-71.00520325,"cca2":"美国","cca1":"US","region":"北美洲","city":"波士顿"},{"iata":"DTW","lat":42.2123985291,"lon":-83.3534011841,"cca2":"美国","cca1":"US","region":"北美洲","city":"底特律"},{"iata":"MSP","lat":44.8819999695,"lon":-93.2218017578,"cca2":"美国","cca1":"US","region":"北美洲","city":"明尼阿波利斯"},{"iata":"MCI","lat":39.2975997925,"lon":-94.7138977051,"cca2":"美国","cca1":"US","region":"北美洲","city":"堪萨斯城"},{"iata":"STL","lat":38.7486991882,"lon":-90.3700027466,"cca2":"美国","cca1":"US","region":"北美洲","city":"圣路易斯"},{"iata":"OMA","lat":41.3031997681,"lon":-95.8940963745,"cca2":"美国","cca1":"US","region":"北美洲","city":"奥马哈"},{"iata":"LAS","lat":36.08010101,"lon":-115.1520004,"cca2":"美国","cca1":"US","region":"北美洲","city":"拉斯维加斯"},{"iata":"EWR","lat":40.6925010681,"lon":-74.1687011719,"cca2":"美国","cca1":"US","region":"北美洲","city":"纽瓦克"},{"iata":"ABQ","lat":35.0844,"lon":-106.6504,"cca2":"美国","cca1":"US","region":"北美洲","city":"阿尔伯克基"},{"iata":"BUF","lat":42.94049835,"lon":-78.73220062,"cca2":"美国","cca1":"US","region":"北美洲","city":"布法罗"},{"iata":"CLT","lat":35.2140007019,"lon":-80.9430999756,"cca2":"美国","cca1":"US","region":"北美洲","city":"夏洛特敦"},{"iata":"CMH","lat":39.9980010986,"lon":-82.8918991089,"cca2":"美国","cca1":"US","region":"北美洲","city":"哥伦布"},{"iata":"PDX","lat":45.58869934,"lon":-122.5979996,"cca2":"美国","cca1":"US","region":"北美洲","city":"波特兰"},{"iata":"PHL","lat":39.8718986511,"lon":-75.2410964966,"cca2":"美国","cca1":"US","region":"北美洲","city":"费城"},{"iata":"PIT","lat":40.49150085,"lon":-80.23290253,"cca2":"美国","cca1":"US","region":"北美洲","city":"匹兹堡"},{"iata":"FSD","lat":43.540819819502,"lon":-96.65511577730963,"cca2":"美国","cca1":"US","region":"北美洲","city":"苏瀑布"},{"iata":"MEM","lat":35.0424003601,"lon":-89.9766998291,"cca2":"美国","cca1":"US","region":"北美洲","city":"孟菲斯"},{"iata":"BNA","lat":36.1245002747,"lon":-86.6781997681,"cca2":"美国","cca1":"US","region":"北美洲","city":"纳什维尔"},{"iata":"AUS","lat":30.1975,"lon":-97.6664,"cca2":"美国","cca1":"US","region":"北美洲","city":"奥斯汀"},{"iata":"DFW","lat":32.8968009949,"lon":-97.0380020142,"cca2":"美国","cca1":"US","region":"北美洲","city":"达拉斯"},{"iata":"IAH","lat":29.9843997955,"lon":-95.3414001465,"cca2":"美国","cca1":"US","region":"北美洲","city":"休斯顿"},{"iata":"MFE","lat":26.17580032,"lon":-98.23860168,"cca2":"美国","cca1":"US","region":"北美洲","city":"麦卡伦"},{"iata":"SLC","lat":40.7883987427,"lon":-111.977996826,"cca2":"美国","cca1":"US","region":"北美洲","city":"盐湖城"},{"iata":"IAD","lat":38.94449997,"lon":-77.45580292,"cca2":"美国","cca1":"US","region":"北美洲","city":"阿什本"},{"iata":"ORF","lat":36.8945999146,"lon":-76.2012023926,"cca2":"美国","cca1":"US","region":"北美洲","city":"诺福克"},{"iata":"RIC","lat":37.5051994324,"lon":-77.3197021484,"cca2":"美国","cca1":"US","region":"北美洲","city":"里士满"},{"iata":"SEA","lat":47.4490013123,"lon":-122.308998108,"cca2":"美国","cca1":"US","region":"北美洲","city":"西雅图"},{"iata":"TAS","lat":41.257900238,"lon":69.2811965942,"cca2":"乌兹别克斯坦","cca1":"UZ","region":"亚太","city":"塔什干"},{"iata":"HAN","lat":21.221200943,"lon":105.806999206,"cca2":"越南","cca1":"VN","region":"亚太","city":"河内"},{"iata":"SGN","lat":10.8187999725,"lon":106.652000427,"cca2":"越南","cca1":"VN","region":"亚太","city":"胡志明市"},{"iata":"HRE","lat":-17.9318008423,"lon":31.0928001404,"cca2":"津巴布韦","cca1":"ZW","region":"非洲","city":"哈拉雷"}]` 96 | 97 | 98 | json.Unmarshal([]byte(body), &locations) 99 | 100 | //////////////////////////////////////////// 101 | 102 | locationMap := make(map[string]location) 103 | for _, loc := range locations { 104 | locationMap[loc.Iata] = loc 105 | } 106 | 107 | ips, err := readIPs(*File) 108 | if err != nil { 109 | fmt.Printf("无法从文件中读取 IP: %v\n", err) 110 | return 111 | } 112 | 113 | var wg sync.WaitGroup 114 | wg.Add(len(ips)) 115 | 116 | resultChan := make(chan result, len(ips)) 117 | 118 | thread := make(chan struct{}, *maxThreads) 119 | 120 | var count int 121 | total := len(ips) 122 | 123 | for _, ip := range ips { 124 | thread <- struct{}{} 125 | go func(ip string) { 126 | defer func() { 127 | <-thread 128 | wg.Done() 129 | count++ 130 | percentage := float64(count) / float64(total) * 100 131 | fmt.Printf("已完成: %d 总数: %d 已完成: %.2f%%\r", count, total, percentage) 132 | if count == total { 133 | fmt.Printf("已完成: %d 总数: %d 已完成: %.2f%%\n", count, total, percentage) 134 | } 135 | }() 136 | 137 | parts := strings.Fields(ip) 138 | if len(parts) != 2 { 139 | fmt.Printf("IP地址格式错误: %s\n", ip) 140 | return 141 | } 142 | ipAddr := parts[0] 143 | portStr := parts[1] 144 | 145 | port, err := strconv.Atoi(portStr) 146 | if err != nil { 147 | fmt.Printf("端口格式错误: %s\n", portStr) 148 | return 149 | } 150 | 151 | dialer := &net.Dialer{ 152 | Timeout: timeout, 153 | KeepAlive: 0, 154 | } 155 | start := time.Now() 156 | conn, err := dialer.Dial("tcp", net.JoinHostPort(ipAddr, strconv.Itoa(port))) 157 | if err != nil { 158 | return 159 | } 160 | defer conn.Close() 161 | 162 | // 计算并检查TCP连接延迟 163 | tcpDuration := time.Since(start) 164 | if tcpDuration.Milliseconds() > int64(*delay) { 165 | return // 超过延迟阈值直接 166 | } 167 | // 记录通过延迟检查的有效IP 168 | atomic.AddInt32(&validCount, 1) 169 | start = time.Now() 170 | client := http.Client{ 171 | Transport: &http.Transport{ 172 | Dial: func(network, addr string) (net.Conn, error) { 173 | return conn, nil 174 | }, 175 | }, 176 | Timeout: timeout, 177 | } 178 | 179 | var protocol string 180 | if *enableTLS { 181 | protocol = "https://" 182 | } else { 183 | protocol = "http://" 184 | } 185 | requestURL := protocol + *TCPurl + "/cdn-cgi/trace" 186 | req, _ := http.NewRequest("GET", requestURL, nil) 187 | 188 | // 添加用户代理 189 | req.Header.Set("User-Agent", "Mozilla/5.0") 190 | req.Close = true 191 | resp, err := client.Do(req) 192 | if err != nil { 193 | return 194 | } 195 | 196 | duration := time.Since(start) 197 | if duration > maxDuration { 198 | return 199 | } 200 | 201 | defer func(Body io.ReadCloser) { 202 | err := Body.Close() 203 | if err != nil { 204 | 205 | } 206 | }(resp.Body) 207 | body, err := io.ReadAll(resp.Body) 208 | if err != nil { 209 | return 210 | } 211 | 212 | if strings.Contains(string(body), "uag=Mozilla/5.0") { 213 | if matches := regexp.MustCompile(`colo=([A-Z]+)`).FindStringSubmatch(string(body)); len(matches) > 1 { 214 | dataCenter := matches[1] 215 | loc, ok := locationMap[dataCenter] 216 | if ok { 217 | fmt.Printf("发现有效IP %s 端口 %d 位置信息 %s 延迟 %d 毫秒\n", ipAddr, port, loc.City, tcpDuration.Milliseconds()) 218 | resultChan <- result{ipAddr, port, dataCenter, loc.Region, loc.Cca1, loc.Cca2, loc.City, fmt.Sprintf("%d ms", tcpDuration.Milliseconds()), tcpDuration} 219 | } else { 220 | fmt.Printf("发现有效IP %s 端口 %d 位置信息未知 延迟 %d 毫秒\n", ipAddr, port, tcpDuration.Milliseconds()) 221 | resultChan <- result{ipAddr, port, dataCenter, "", "", "", "", fmt.Sprintf("%d ms", tcpDuration.Milliseconds()), tcpDuration} 222 | } 223 | } 224 | } 225 | }(ip) 226 | } 227 | 228 | wg.Wait() 229 | close(resultChan) 230 | 231 | if len(resultChan) == 0 { 232 | fmt.Println("没有发现有效的IP") 233 | return 234 | } 235 | var results []speedtestresult 236 | if *speedTest > 0 { 237 | fmt.Printf("找到符合条件的ip 共%d个\n", atomic.LoadInt32(&validCount)) 238 | fmt.Printf("开始测速\n") 239 | var wg2 sync.WaitGroup 240 | wg2.Add(*speedTest) 241 | count = 0 242 | total := len(resultChan) 243 | results = []speedtestresult{} 244 | for i := 0; i < *speedTest; i++ { 245 | thread <- struct{}{} 246 | go func() { 247 | defer func() { 248 | <-thread 249 | wg2.Done() 250 | }() 251 | for res := range resultChan { 252 | 253 | downloadSpeed := getDownloadSpeed(res.ip, res.port) 254 | results = append(results, speedtestresult{result: res, downloadSpeed: downloadSpeed}) 255 | 256 | count++ 257 | percentage := float64(count) / float64(total) * 100 258 | fmt.Printf("已完成: %.2f%%\r", percentage) 259 | if count == total { 260 | fmt.Printf("已完成: %.2f%%\033[0\n", percentage) 261 | } 262 | } 263 | }() 264 | } 265 | wg2.Wait() 266 | } else { 267 | for res := range resultChan { 268 | results = append(results, speedtestresult{result: res}) 269 | } 270 | } 271 | 272 | if *speedTest > 0 { 273 | sort.Slice(results, func(i, j int) bool { 274 | return results[i].downloadSpeed > results[j].downloadSpeed 275 | }) 276 | } else { 277 | sort.Slice(results, func(i, j int) bool { 278 | return results[i].result.tcpDuration < results[j].result.tcpDuration 279 | }) 280 | } 281 | 282 | file, err := os.Create(*outFile) 283 | if err != nil { 284 | fmt.Printf("无法创建文件: %v\n", err) 285 | return 286 | } 287 | defer func(file *os.File) { 288 | err := file.Close() 289 | if err != nil { 290 | 291 | } 292 | }(file) 293 | // 写入UTF-8 BOM 294 | _, err = file.WriteString("\xEF\xBB\xBF") 295 | if err != nil { 296 | return 297 | } 298 | writer := csv.NewWriter(file) 299 | if *speedTest > 0 { 300 | writer.Write([]string{"IP地址", "端口", "TLS", "数据中心", "地区", "国际代码", "国家", "城市", "网络延迟", "下载速度MB/s"}) 301 | } else { 302 | writer.Write([]string{"IP地址", "端口", "TLS", "数据中心", "地区", "国际代码", "国家", "城市", "网络延迟"}) 303 | } 304 | for _, res := range results { 305 | if *speedTest > 0 { 306 | if res.downloadSpeed >= float64(*speedLimit) { 307 | writer.Write([]string{res.result.ip, strconv.Itoa(res.result.port), strconv.FormatBool(*enableTLS), res.result.dataCenter, res.result.region, res.result.cca1, res.result.cca2, res.result.city, res.result.latency, fmt.Sprintf("%.2f", res.downloadSpeed)}) 308 | } 309 | } else { 310 | writer.Write([]string{res.result.ip, strconv.Itoa(res.result.port), strconv.FormatBool(*enableTLS), res.result.dataCenter, res.result.region, res.result.cca1, res.result.cca2, res.result.city, res.result.latency}) 311 | } 312 | } 313 | writer.Flush() 314 | // 清除输出内容 315 | fmt.Print("\033[2J") 316 | fmt.Printf("有效IP数量: %d | 成功将结果写入文件 %s,耗时 %d秒\n", 317 | atomic.LoadInt32(&validCount), *outFile, time.Since(startTime)/time.Second) 318 | } 319 | 320 | // 从文件中读取IP地址和端口 321 | func readIPs(File string) ([]string, error) { 322 | file, err := os.Open(File) 323 | if err != nil { 324 | return nil, err 325 | } 326 | defer func(file *os.File) { 327 | err := file.Close() 328 | if err != nil { 329 | 330 | } 331 | }(file) 332 | var ips []string 333 | scanner := bufio.NewScanner(file) 334 | for scanner.Scan() { 335 | line := scanner.Text() 336 | parts := strings.Fields(line) 337 | if len(parts) != 2 { 338 | fmt.Printf("行格式错误: %s\n", line) 339 | continue 340 | } 341 | ipAddr := parts[0] 342 | portStr := parts[1] 343 | 344 | port, err := strconv.Atoi(portStr) 345 | if err != nil { 346 | fmt.Printf("端口格式错误: %s\n", portStr) 347 | continue 348 | } 349 | 350 | ip := fmt.Sprintf("%s %d", ipAddr, port) 351 | ips = append(ips, ip) 352 | } 353 | return ips, scanner.Err() 354 | } 355 | 356 | // 测速函数 357 | func getDownloadSpeed(ip string, port int) float64 { 358 | var protocol string 359 | if *enableTLS { 360 | protocol = "https://" 361 | } else { 362 | protocol = "http://" 363 | } 364 | speedTestURL := protocol + *speedTestURL 365 | // 创建请求 366 | req, _ := http.NewRequest("GET", speedTestURL, nil) 367 | req.Header.Set("User-Agent", "Mozilla/5.0") 368 | 369 | // 创建TCP连接 370 | dialer := &net.Dialer{ 371 | Timeout: timeout, 372 | KeepAlive: 0, 373 | } 374 | conn, err := dialer.Dial("tcp", net.JoinHostPort(ip, strconv.Itoa(port))) 375 | if err != nil { 376 | return 0 377 | } 378 | defer func(conn net.Conn) { 379 | err := conn.Close() 380 | if err != nil { 381 | 382 | } 383 | }(conn) 384 | 385 | fmt.Printf("正在测试IP %s 端口 %d\n", ip, port) 386 | startTime := time.Now() 387 | // 创建HTTP客户端 388 | client := http.Client{ 389 | Transport: &http.Transport{ 390 | Dial: func(network, addr string) (net.Conn, error) { 391 | return conn, nil 392 | }, 393 | }, 394 | //设置单个IP测速最长时间为5秒 395 | Timeout: 5 * time.Second, 396 | } 397 | // 发送请求 398 | req.Close = true 399 | resp, err := client.Do(req) 400 | if err != nil { 401 | fmt.Printf("IP %s 端口 %d 测速无效\n", ip, port) 402 | return 0 403 | } 404 | defer func(Body io.ReadCloser) { 405 | err := Body.Close() 406 | if err != nil { 407 | 408 | } 409 | }(resp.Body) 410 | 411 | // 复制响应体到/dev/null,并计算下载速度 412 | written, _ := io.Copy(io.Discard, resp.Body) 413 | duration := time.Since(startTime) 414 | speed := float64(written) / duration.Seconds() / 1024 / 1024 415 | 416 | // 输出结果 417 | fmt.Printf("IP %s 端口 %d 下载速度 %.2f MB/s\n", ip, port, speed) 418 | return speed 419 | } 420 | --------------------------------------------------------------------------------