Stanford NLP - 命名实体识别 - NERClassifierCombiner

命名实体识别 - NERClassifierCombiner

描述

在文本中识别命名实体(人员和公司名称等)。原则上,该注释器使用一个或多个机器学习序列模型来标记实体,但是它也可以调用基于规则的专家组件,例如用于标记和解释时间和日期。需要标准化的数字实体(例如,日期)将其标准化值存储在NormalizedNamedEntityTagAnnotation中。要获得对基于规则的NER的更广泛支持,您可能还需要查看RegexNER注释器。被识别的实体集合取决于语言,并且对于除了下面针对英语描述的其他语言,所识别的实体集合通常更受限制。正如名称“NERClassifierCombiner”所暗示的那样,通常此注释器将运行多个命名实体识别器,然后组合它们的结果,但它只能运行一个注释器或仅运行基于规则的数量NER。

对于英语,默认情况下,此注释器识别命名(PERSON,LOCATION,ORGANIZATION,MISC),数字(MONEY,NUMBER,ORDINAL,PERCENT)和时间(DATE,TIME,DURATION,SET)实体(12个类)。添加regexner注释器并使用提供的RegexNER模式文件添加了对细粒度和附加实体类的支持EMAIL,URL,CITY,STATE_OR_PROVINCE,COUNTRY,NATIONALITY,RELIGION,(作业)TITLE,IDEOLOGY,CRIMINAL_CHARGE,CAUSE_OF_DEATH(11个类)共23课。使用在各种语料库上训练的三个CRF序列标记符的组合来识别命名实体,包括CoNLL,ACE,MUC和ERE语料库。使用基于规则的系统识别数字实体。

物业名称 注释器类名称 生成的注释
NER NERClassifierCombiner NamedEntityTagAnnotation和NormalizedNamedEntityTagAnnotation

选项

选项名称 类型 默认 描述
ner.model list(字符串) 空值 以逗号分隔的NER模型名称列表(或者只是一个名称就可以了)。如果未指定,则使用默认的英语模型列表(3class,7class和MISCclass,按此顺序)。这些名称将作为类路径资源,文件名或URL进行查找。
ner.applyNumericClassifiers 布尔 真正 是否使用数字分类器,包括钱,百分比,数字,包括SUTime。这些是针对英语的硬编码,因此如果使用其他语言,则应将其设置为false。
ner.applyFineGrained 布尔 真正 是否应用细粒度NER标签(例如LOCATION - > CITY); 这会降低性能
ner.buildEntityMentions 布尔 真正 是否从令牌NER标签构建实体提及
ner.combinationMode 正常 当设置为NORMAL时,每个标记只能由应用该标记的第一个CRF分类器应用; 当设置为HIGH_RECALL时,所有CRF分类器都可以应用它们的所有标记
ner.useSUTime 布尔 真正 是否使用SUTime。SUTime目前只支持英语; 如果不处理英语,请务必将其设置为false。
sutime.markTimeRanges 布尔 告诉SUTime是否将诸如“从1月到3月”的短语标记为范围,而不是分别标记“1月”和“3月”。
sutime.includeRange 布尔 如果标记时间范围,请在SUSTime中设置TIMEX输出中的时间范围。
maxAdditionalKnownLCWords INT - 限制已知小写字集的大小。将此设置为0以防止排序问题(即,当在document1上非零运行时,document2可能与在document2上运行的结果不同,然后document1

NER管道概述

完整的命名实体识别管道变得相当复杂,涉及一组集成统计和基于规则的方法的不同阶段。以下是这些不同阶段的细分。

运行此过程的主要类是 edu.stanford.nlp.pipeline.NERCombinerAnnotator

统计模型

在此阶段,将对每个句子运行一系列训练有素的CRF。这些CRF是在大型标记数据集上训练的。他们评估整个序列并选择最佳标签序列。

这些是运行的默认模型:

# tags: LOCATION, ORGANIZATION, PERSON
edu/stanford/nlp/models/ner/english.all.3class.distsim.crf.ser.gz
# tags: DATE, LOCATION, MONEY, ORGANIZATION, PERCENT, PERSON, TIME
edu/stanford/nlp/models/ner/english.muc.7class.distsim.crf.ser.gz
# LOCATION, MISC, ORGANIZATION, PERSON
edu/stanford/nlp/models/ner/english.conll.4class.distsim.crf.ser.gz

由一个模型编写的标签不能被系列中的后续模型覆盖。

模型的组合方式有两种选择。这些都是与ner.combinationMode 酒店一起选择的。

  • NORMAL - 任何给定的标签只能由一个模型应用(第一个应用标签的模型)
  • HIGH_RECALL - 所有模型都可以应用所有标签

因此,例如,如果ner.combinationMode设置为NORMAL,则仅应用3类模型的ORGANIZATION标记。如果设置为HIGH_RECALL,则还将应用7级和4级模型的ORGANIZATION标签。

如果您不想运行任何统计模型,请设置ner.model为空字符串。

数字序列和SUTime

接下来,运行一系列基于规则的系统来识别和标记数字序列和时间相关序列。

此阶段默认运行,但可以通过设置ner.applyNumericClassifiers为停用false

这会产生诸如的标签 NUMBER, ORDINAL, MONEY, DATE, and TIME

运行此阶段的类是 edu.stanford.nlp.ie.regexp.NumberSequenceClassifier

默认情况下也使用SUTime(下面更详细地描述)。您可以通过设置ner.useSUTime为停用此功能false

细粒度NER

此时,将运行一系列用于KBP 2017竞赛的规则,以创建更细粒度的NER标签。使用TokensRegexNERAnnotator子注释器应用这些规则。这是主要 NERCombinerAnnotator构建一个TokensRegexNERAnnotator作为子注释器并在所有句子上运行它作为其整个标记过程的一部分。

TokensRegexNERAnnotator运行TokensRegex规则。您可以在此处查看TokensRegexNERAnnotator的所有设置 。

注意:应用这些规则将显着减慢标记过程。

此阶段设置的标签包括:

CAUSE_OF_DEATH, CITY, COUNTRY, CRIMINAL_CHARGE, EMAIL, 
IDEOLOGY, NATIONALITY, RELIGION, STATE_OR_PROVINCE, TITLE, URL

如果您不想运行细粒度规则,请设置ner.applyFineGrainedfalse

RegexNER规则格式

还有一个更详细的写了约RegexNER 这里

格式是一系列制表符分隔的列。

第一列是标记模式,第二列是要应用的NER标记,第三列是可以覆盖的NER标记的类型,第四列是用于打破平局的优先级(如果两个规则与序列匹配)。

每个以空格分隔的条目表示匹配令牌的正则表达式。

规则(记住这些是制表符分隔的列):

Los Angeles	CITY	LOCATION,MISC	1.0

表示匹配令牌“Los”后跟令牌“Angeles”,并将它们标记为CITY,前提是它们具有O,LOCATION或MISC的当前NER标记。

规则:

Bachelor of (Arts|Science)	DEGREE	MISC	1.0

意味着匹配令牌“Bachelor”,然后是令牌“of”,最后是令牌“Arts”或“Science”。

自定义细粒度NER

以下是如何自定义细粒度NER的细分。整个ner注释器创建一个子注释器ner.fine.regexner,它是一个实例TokensRegexNERAnnotator

ner.fine.regexner.mapping属性允许为每个规则文件指定一组规则文件和其他属性。

格式如下:

  1. 对于每个规则文件,都有一个以逗号分隔的选项列表,以rules文件的路径结尾
  2. 规则文件的每个条目由a分隔 ;

例如,这是默认ner.fine.regexner.mapping设置:

ignorecase=true,validpospattern=^(NN|JJ).*,edu/stanford/nlp/models/kbp/english/gazetteers/regexner_caseless.tab;edu/stanford/nlp/models/kbp/english/gazetteers/regexner_cased.tab

这两个规则文件是:

edu/stanford/nlp/models/kbp/english/gazetteers/regexner_caseless.tab
edu/stanford/nlp/models/kbp/english/gazetteers/regexner_cased.tab

选项edu/stanford/nlp/models/kbp/english/gazetteers/regexner_caseless.tab是:

ignorecase=true,validpospattern=^(NN|JJ).*

虽然edu/stanford/nlp/models/kbp/english/gazetteers/regexner_cased.tab在这个例子中没有设置选项。

以下是对使用的TokensRegexNERAnnotator子注释器的一些常见选项的描述ner

你可以找到在网页上详细信息TokensRegexNERAnnotator位于这里

选项名称 类型 默认 描述
忽略大小写 布尔 使模式不区分大小写?
validpospattern 正则表达式 空值 必须匹配的词性标记模式

如果要设置将应用于所有规则文件的全局设置,请记住使用ner.fine.regexner.ignorecase 和ner.fine.regexner.validpospattern。如果要使用该ner.fine.regexner.mapping选项设置特定规则文件的 选项,请按照上面的模式进行操作。

额外的TokensRegexNER规则

在运行细粒度规则之后,还有一个选项供用户指定他们希望在细粒度NER阶段之后运行的其他规则。

第二TokensRegexNERAnnotator个子注释器具有名称ner.additional.regexner并以相同方式定制。这适用于用户希望在我们提供的标准规则之后运行自己的规则的情况。

例如,假设您希望在之前的NER步骤运行后匹配运动队。

您的规则文件可能如下所示 /path/to/sports_teams.rules

Boston Red Sox       SPORTS_TEAM     ORGANIZATION<strong>,</strong>MISC       <span style="color:#009999">1
Denver Broncos       SPORTS_TEAM     ORGANIZATION<strong>,</strong>MISC       <span style="color:#009999">1</span>
Detroit Red Wings    SPORTS_TEAM     ORGANIZATION<strong>,</strong>MISC       <span style="color:#009999">1</span>
Los Angeles Lakers   SPORTS_TEAM     ORGANIZATION<strong>,</strong>MISC       <span style="color:#009999">1</span>
</code></span></span>

您可以通过设置ner.additional.regexner.mapping将其集成到整个NER流程中 /path/to/sports_teams.rules

默认情况下,不会运行其他规则,因此保留ner.additional.regexner.mapping空白将导致此阶段根本不运行。

额外的TokensRegex规则

如果要在构建实体之前运行一系列TokensRegex规则,还可以指定一组TokensRegex规则。一个TokensRegexAnnotator子标注将被调用。它有名字ner.additional.tokensregex

示例命令:

<span style="color:#333333"><span style="color:#77777a"><code>java <span style="color:#000080">-Xmx5g</span> edu.stanford.nlp.pipeline.StanfordCoreNLP <span style="color:#000080">-annotators</span> tokenize,ssplit,pos,lemma,ner <span style="color:#000080">-ner</span>.additional.tokensregex.rules example.rules <span style="color:#000080">-file</span> example.txt <span style="color:#000080">-outputFormat</span> text
</code></span></span>

您可以在此处了解有关TokensRegex规则的更多信息

实体提及检测

在运行所有先前步骤之后,将运行实体检测以将标记的标记组合成实体。实体提及检测将基于标记方案。这是通过EntityMentionsAnnotator 子注释器完成的。

您可以在此处找到此注释器的更详细说明

如果使用基本IO标记方案(例如:PERSON,ORGANIZATION,LOCATION),则具有相同标记的所有连续标记序列将被标记为实体。

如果使用更高级的标记方案(例如带有像B-PERSON和I-PERSON这样的标记的BIO),具有由B标记分割的相同标记的序列将变成多个实体。

我们所有的模型和规则文件都使用基本标记方案,但您可以创建自己的模型和使用BIO的规则。

例如,(Joe PERSON) (Smith PERSON) (Jane PERSON) (Smith PERSON)将创建实体Joe Smith Jane Smith

另一方面,(Joe B-PERSON) (Smith I-PERSON) (Jane B-PERSON) (Smith I-PERSON)将创建两个实体:Joe SmithJane Smith

您可以通过ner.buildEntityMentions设置为停用此功能false

此时,NER过程将完成,标记带有NER标记和创建实体的标记。

命令行示例

<span style="color:#333333"><span style="color:#77777a"><code><span style="color:#999988"><em># run default NER</em></span>
java <span style="color:#000080">-Xmx4g</span> <span style="color:#000080">-cp</span> <span style="color:#dd1144">"*"</span> edu.stanford.nlp.pipeline.StanfordCoreNLP <span style="color:#000080">-annotators</span> tokenize,ssplit,pos,lemma,ner <span style="color:#000080">-file</span> example.txt <span style="color:#000080">-outputFormat</span> text
</code></span></span>
<span style="color:#333333"><span style="color:#77777a"><code><span style="color:#999988"><em># shut off numeric classifiers</em></span>
<span style="color:#999988"><em># note that in this case ner no longer requires pos or lemma</em></span>
java <span style="color:#000080">-Xmx4g</span> <span style="color:#000080">-cp</span> <span style="color:#dd1144">"*"</span> edu.stanford.nlp.pipeline.StanfordCoreNLP <span style="color:#000080">-annotators</span> tokenize,ssplit,ner <span style="color:#000080">-ner</span>.applyNumericClassifiers <span style="color:#0086b3">false</span> <span style="color:#000080">-file</span> example.txt <span style="color:#000080">-outputFormat</span> text
</code></span></span>
<span style="color:#333333"><span style="color:#77777a"><code><span style="color:#999988"><em># shut off SUTime</em></span>
java <span style="color:#000080">-Xmx4g</span> <span style="color:#000080">-cp</span> <span style="color:#dd1144">"*"</span> edu.stanford.nlp.pipeline.StanfordCoreNLP <span style="color:#000080">-annotators</span> tokenize,ssplit,pos,lemma,ner <span style="color:#000080">-ner</span>.useSUTime <span style="color:#0086b3">false</span> <span style="color:#000080">-file</span> example.txt <span style="color:#000080">-outputFormat</span> text
</code></span></span>
<span style="color:#333333"><span style="color:#77777a"><code><span style="color:#999988"><em># shut off fine grained NER</em></span>
java <span style="color:#000080">-Xmx4g</span> <span style="color:#000080">-cp</span> <span style="color:#dd1144">"*"</span> edu.stanford.nlp.pipeline.StanfordCoreNLP <span style="color:#000080">-annotators</span> tokenize,ssplit,pos,lemma,ner <span style="color:#000080">-ner</span>.applyFineGrained <span style="color:#0086b3">false</span> <span style="color:#000080">-file</span> example.txt <span style="color:#000080">-outputFormat</span> text
</code></span></span>
<span style="color:#333333"><span style="color:#77777a"><code><span style="color:#999988"><em># run fine-grained NER with a custom rules file</em></span>
java <span style="color:#000080">-Xmx4g</span> <span style="color:#000080">-cp</span> <span style="color:#dd1144">"*"</span> edu.stanford.nlp.pipeline.StanfordCoreNLP <span style="color:#000080">-annotators</span> tokenize,ssplit,pos,lemma,ner <span style="color:#000080">-ner</span>.fine.regexner.mapping custom.rules <span style="color:#000080">-file</span> example.txt <span style="color:#000080">-outputFormat</span> text
</code></span></span>
<span style="color:#333333"><span style="color:#77777a"><code><span style="color:#999988"><em># run fine-grained NER with two custom rules files</em></span>
<span style="color:#999988"><em># the first rules file caseless.rules should be case-insensitive, the second rules file uses default options</em></span>
java <span style="color:#000080">-Xmx4g</span> <span style="color:#000080">-cp</span> <span style="color:#dd1144">"*"</span> edu.stanford.nlp.pipeline.StanfordCoreNLP <span style="color:#000080">-annotators</span> tokenize,ssplit,pos,lemma,ner <span style="color:#000080">-ner</span>.fine.regexner.mapping <span style="color:#dd1144">"ignorecase=true,caseless.rules;cased.rules"</span> <span style="color:#000080">-file</span> example.txt <span style="color:#000080">-outputFormat</span> text
</code></span></span>
<span style="color:#333333"><span style="color:#77777a"><code><span style="color:#999988"><em># add additional rules to run after fine-grained NER</em></span>
java <span style="color:#000080">-Xmx4g</span> <span style="color:#000080">-cp</span> <span style="color:#dd1144">"*"</span> edu.stanford.nlp.pipeline.StanfordCoreNLP <span style="color:#000080">-annotators</span> tokenize,ssplit,pos,lemma,ner <span style="color:#000080">-ner</span>.additional.regexner.mapping additional.rules <span style="color:#000080">-file</span> example.txt <span style="color:#000080">-outputFormat</span> text
</code></span></span>
<span style="color:#333333"><span style="color:#77777a"><code><span style="color:#999988"><em># run tokens regex rules</em></span>
java <span style="color:#000080">-Xmx5g</span> edu.stanford.nlp.pipeline.StanfordCoreNLP <span style="color:#000080">-annotators</span> tokenize,ssplit,pos,lemma,ner <span style="color:#000080">-ner</span>.additional.tokensregex.rules example.rules <span style="color:#000080">-file</span> example.txt <span style="color:#000080">-outputFormat</span> text
</code></span></span>
<span style="color:#333333"><span style="color:#77777a"><code><span style="color:#999988"><em># don't build entity mentions</em></span>
java <span style="color:#000080">-Xmx4g</span> <span style="color:#000080">-cp</span> <span style="color:#dd1144">"*"</span> edu.stanford.nlp.pipeline.StanfordCoreNLP <span style="color:#000080">-annotators</span> tokenize,ssplit,pos,lemma,ner <span style="color:#000080">-ner</span>.buildEntityMentions <span style="color:#0086b3">false</span> <span style="color:#000080">-file</span> example.txt <span style="color:#000080">-outputFormat</span> text
</code></span></span>

Java API示例

package edu.stanford.nlp.examples;

import edu.stanford.nlp.pipeline.*;

import java.util.Properties;
import java.util.stream.Collectors;

public class NERPipelineDemo {

  public static void main(String[] args) {
    // set up pipeline properties
    Properties props = new Properties();
    props.setProperty("annotators", "tokenize,ssplit,pos,lemma,ner");
    // example customizations (these are commented out but you can uncomment them to see the results

    // disable fine grained ner
    //props.setProperty("ner.applyFineGrained", "false");

    // customize fine grained ner
    //props.setProperty("ner.fine.regexner.mapping", "example.rules");
    //props.setProperty("ner.fine.regexner.ignorecase", "true");

    // add additional rules
    //props.setProperty("ner.additional.regexner.mapping", "example.rules");
    //props.setProperty("ner.additional.regexner.ignorecase", "true");

    // add 2 additional rules files ; set the first one to be case-insensitive
    //props.setProperty("ner.additional.regexner.mapping", "ignorecase=true,example_one.rules;example_two.rules");

    // set up pipeline
    StanfordCoreNLP pipeline = new StanfordCoreNLP(props);
    // make an example document
    CoreDocument doc = new CoreDocument("Joe Smith is from Seattle.");
    // annotate the document
    pipeline.annotate(doc);
    // view results
    System.out.println("---");
    System.out.println("entities found");
    for (CoreEntityMention em : doc.entityMentions())
      System.out.println("\tdetected entity: \t"+em.text()+"\t"+em.entityType());
    System.out.println("---");
    System.out.println("tokens and ner tags");
    String tokensAndNERTags = doc.tokens().stream().map(token -> "("+token.word()+","+token.ner()+")").collect(
        Collectors.joining(" "));
    System.out.println(tokensAndNERTags);
  }

}

SUTime

Stanford CoreNLP包括斯坦福大学的时间表达识别器SUTime。SUTime从“ner”注释器透明调用,因此不需要配置。此外,“cleanxml”注释器可以提取给定XML文档的引用日期,因此相对日期(例如“昨天”)被透明地标准化,无需配置。

SUTime如前支持相同的注释,即NamedEntityTagAnnotation设置用数字实体的标签(日期,时间,持续时间,金钱,百分比或NUMBER)和NormalizedNamedEntityTagAnnotation被设定为归一化的时间表达的值。

此外,SUTime将TimexAnnotation键设置为edu.stanford.nlp.time.Timex对象,该对象包含相应表达式的TIMEX3字段的完整列表,例如“val”,“alt_val”,“type”,“tid” 。这对于有兴趣恢复完整TIMEX3表达式的开发人员可能很有用。

默认情况下,参考日期是从xml文档中的“datetime”和“date”标记中提取的。要设置要使用的其他标记集,请使用clean.datetags属性。使用API​​时,可以将引用日期添加到Annotationvia edu.stanford.nlp.ling.CoreAnnotations.DocDateAnnotation,但请注意,在处理xml文档时,cleanxml注释器将覆盖DocDateAnnotation文档中指定的if“datetime”或“date”。

设置文档日期

DocDateAnnotator提供了多种用于设置文件的日期选项。该ner注释将运行此注释作为子注释。这些可以通过设置ner.docdate子注释器的属性来指定。

选项 描述
useFixedDate 2019年1月1日 为每个文档提供固定日期。
useMappingFile dates.txt 使用制表符分隔的文件指定文档日期。第一列是文档ID,第二列是日期。
usePresent - 给每个文件提供当前日期。
useRegex 纽约时报 - ([0-9] {4} - [0-9] {2} - [0-9] {2})。XML 指定匹配文件名的正则表达式。第一组将被提取为日期。

获取实体信心

以下示例显示如何访问令牌和实体的标签置信度。每个令牌存储由CRF给出的NER标签的概率,该CRF用于分配标签CoreAnnotations.NamedEntityTagProbsAnnotation.class。每个实体提及包含其跨度中具有最低标签概率的令牌的概率。例如,如果Los Angeles有以下概率:

{word: 'Los', 'tag': 'LOCATION', 'prob': .992} 
{word: 'Angeles', 'tag': 'LOCATION', 'prob': .999}

Los Angeles将为实体分配LOCATION具有置信度的标签.992

以下是访问这些信心的代码。

package edu.stanford.nlp.examples;

import edu.stanford.nlp.ling.*;
import edu.stanford.nlp.pipeline.*;
import java.util.*;

public class NERConfidenceExample {

    public static void main(String[] args) {
        String exampleText = "Joe Smith lives in California.";
        Properties props = new Properties();
        props.setProperty("annotators", "tokenize,ssplit,pos,lemma,ner");
        StanfordCoreNLP pipeline = new StanfordCoreNLP(props);
        CoreDocument document = new CoreDocument(exampleText);
        pipeline.annotate(document);
        // get confidences for entities
        for (CoreEntityMention em : document.entityMentions()) {
            System.out.println(em.text() + "\t" + em.entityTypeConfidences());
        }
        // get confidences for tokens
        for (CoreLabel token : document.tokens()) {
            System.out.println(token.word() + "\t" + token.get(CoreAnnotations.NamedEntityTagProbsAnnotation.class));
        }
    }
}

无壳型号

可以使用忽略大小写的NER模型运行Stanford CoreNLP。我们为英语训练了这样的模型。您可以在Caseless模型页面上找到详细信息 。

Training or retraining new models

train / dev / test数据文件应采用以下格式:

Joe    PERSON
Smith  PERSON
lives  O
in     O
California    LOCATION
.    O

He    O
used    O
to    O
live    O
in    O
Oregon    LOCATION
.    O

在此示例中,每一行都是一个标记,后跟一个标签,后跟NER标记。空行表示句子中断。我们发布的模型经过了超过一百万个令牌的培训。您拥有的培训数据越多,您的模型就越准确。

用于人员/位置/组织/ MISC的标准培训数据集必须从最不发达国家购买,我们不分发它们。

以下是启动培训过程的命令(确保您的CLASSPATH设置为包含所有Stanford CoreNLP罐):

java -Xmx2g -cp "*" edu.stanford.nlp.ie.crf.CRFClassifier -prop ner.model.props

可以使用属性文件自定义培训过程。以下是用于训练英语模型(ner.model.props)的示例属性文件:

# location of training data
trainFileList = /path/to/conll.3class.train
# location of test data
testFile = /path/to/all.3class.test
# where to store the saved model
serializeTo = ner.model.ser.gz

type = crf

wordFunction = edu.stanford.nlp.process.AmericanizeFunction

useDistSim = false

# establish the data file format
map = word=0,answer=1

saveFeatureIndexToDisk = true

useClassFeature=true
useWord=true
useNGrams=true
noMidNGrams=true
maxNGramLeng=6
usePrev=true
useNext=true
useLongSequences=true
useSequences=true
usePrevSequences=true
maxLeft=1
useTypeSeqs=true
useTypeSeqs2=true
useTypeySequences=true
useOccurrencePatterns=true
useLastRealWord=true
useNextRealWord=true
normalize=true
wordShape=chris2useLC
useDisjunctive=true
disjunctionWidth=5

readerAndWriter=edu.stanford.nlp.sequences.ColumnDocumentReaderAndWriter

useObservedSequencesOnly=true

useQN = true
QNsize = 25

# makes it go faster
featureDiffThresh=0.05

有关于培训CRF模型的更多信息,请点击此处

https://stanfordnlp.github.io/CoreNLP/ner.html

上一篇:python内在模块之winreg --- Windows 注册表访问


下一篇:Python 实现windows下自动切换代理IP