ディレクトリ
ディレクトリ
ライブラリやツールの主な用途
requests
aiohttp
lxml
Beautiful Soup
pyquery
asyncio
fake_useragent
pymongo
MongoDB
python3.7
I.はじめに
- Webページの情報をクロールコード分析のページ(情報が10をクロール)。
- エージェント(取得するために異なる分析のリポジトリを使用しての経験
IP:Port
とタイプ)。 - 演技をスクリーニングするために検査を受けます。
- MongoDBのにスクリーニングエージェントの成功。
II。プロセス
(A)分析http://www.xicidaili.com/nn/1ページコード
1.ページ解析
次のように最初のページにクロールのWebページ、IPアドレス、ポート、およびタイプは、クロールに私たちの目標です。
2ページ目に、観察url
の変更を:
見つけることができるurl
からhttp://www.xicidaili.com/nn/1なっhttp://www.xicidaili.com/nn/2ので、以下のページに、リンクを描画することができ、のhttp:// www.xicidaili.com/nn/ページの代わりに数字との背後には、いくつかの最初のものです。
次に、ページ開発者ツールを開いて入力してNetwork
、次のように、ページのコンテンツへのナビゲートを受け取りました:
見つけることができる、我々はクロールされるプロキシ情報は、対に存在する<tr><\tr>
あなたもこれらのラベルを見つけることができ、さらなる分析を続け、タブclass
のいずれか"odd"
、またはである""
が、私たちが望む情報は、ip
アドレスがあるtr
ラベルの下の2番目td
ラベルは、第三のポートが上に配置されているtd
の第六の種類に位置し、ラベルをtd
ラベル。
その後、我々は、それぞれクロール、解決しようとするライブラリを使用し始めることができlxml
、Beautiful Soup
同様にpyquery
解析するための3つの一般的な構文解析ライブラリを。
トップへ2.クロール
使用するrequests
ライブラリを(以降理由理由とプロキシテストプロキシを使用するのではなく、非同期リクエストコルーチンライブラリーになりましたaiohttp
、以下を参照してください。質問:IPアドレスが禁止されている)、最初に直接アクセスしてみてください。
import requests
response = requests.get("http://www.xicidaili.com/nn/1")
print(response.text)
結果は以下の通りであります:
<html>
<head><title>503 Service Temporarily Unavailable</title></head>
<body bgcolor="white">
<center><h1>503 Service Temporarily Unavailable</h1></center>
<hr><center>nginx/1.1.19</center>
</body>
</html>
戻りステータスコード503は、サービスが利用できないため、リクエストヘッダに参加しようとの契約をしなければならないことを示します。
import requests
header = {'User-Agent':"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Safari/537.36"}
response = requests.get("http://www.xicidaili.com/nn/1", headers = header)
print(response.text)
出力:
<!DOCTYPE html>
<html>
<head>
<title>国内高匿免费HTTP代理IP__第1页国内高匿</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<meta name="Description" content="国内高匿免费HTTP代理" />
<meta name="Keywords" content="国内高匿,免费高匿代理,免费匿名代理,隐藏IP" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
<meta name="applicable-device"content="pc,mobile">
......
これは、通常、情報のページを取得します。次に、異なる解析ライブラリページの解像度を使用するように選択します。
(ii)は、異なる分析情報ライブラリのクロールを使用して
1. lxml
解析するためのライブラリ
import requests
def get_page():
try:
header = {'User-Agent':"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Safari/537.36"}
response = requests.get("http://www.xicidaili.com/nn/1", headers = header)
get_detail(response.text)
except Exception as e:
print("发生错误: ", e)
# 使用lxml爬取
from lxml import etree
def get_detail(html):
html = etree.HTML(html)
# 爬取ip地址信息
print(html.xpath('//tr[@class="odd" or @class=""]/td[2]/text()'))
if __name__ == "__main__":
get_page()
まず、XPathのルール使用して、最初のページのすべてのIPアドレス情報を取得しようと'//tr[@class="odd"]/td[2]/text()'
抽出結果を次のとおりです。
['121.40.66.129', '117.88.177.132', '117.88.176.203', '218.21.230.156', '121.31.101.41', '60.205.188.24', '221.206.100.133', '27.154.34.146', '58.254.220.116', '39.91.8.31', '221.218.102.146', '223.10.21.0', '58.56.149.198', '219.132.205.105', '221.237.37.97', '183.163.24.15', '171.80.196.14', '118.114.96.251', '114.239.91.166', '111.222.141.127', '121.237.148.133', '123.168.67.126', '118.181.226.166', '121.237.148.190', '124.200.36.118', '58.58.213.55', '49.235.253.240', '183.147.11.34', '121.40.162.239', '121.237.148.139', '121.237.148.118', '117.88.5.174', '117.88.5.234', '117.87.180.144', '119.254.94.93', '60.2.44.182', '175.155.239.23', '121.237.148.156', '118.78.196.186', '123.118.108.201', '117.88.4.71', '113.12.202.50', '117.88.177.34', '117.88.4.35', '222.128.9.235', '121.237.148.131', '121.237.149.243', '121.237.148.8', '182.61.179.157', '175.148.68.133']
結果はエラー、あなたがポートと種類を得ることができるのと同じ方法ではありません。
from lxml import etree
def get_detail(html):
html = etree.HTML(html)
# 爬取ip地址信息
print(html.xpath('//tr[@class="odd" or @class=""]/td[2]/text()')[:10])
# 爬取端口信息
print(html.xpath('//tr[@class="odd" or @class=""]/td[3]/text()')[:10])
# 爬取类型信息
print(html.xpath('//tr[@class="odd" or @class=""]/td[6]/text()')[:10])
# 统计一页有多少条数据
print(len(html.xpath('//tr[@class="odd" or @class=""]/td[6]/text()')))
結果として、表示出力データ、100の全
['121.237.149.117', '121.237.148.87', '59.44.78.30', '124.93.201.59', '1.83.117.56', '117.88.176.132', '121.40.66.129', '222.95.144.201', '117.88.177.132', '121.237.149.132']
['3000', '3000', '42335', '59618', '8118', '3000', '808', '3000', '3000', '3000']
['HTTP', 'HTTP', 'HTTP', 'HTTPS', 'HTTP', 'HTTP', 'HTTP', 'HTTP', 'HTTP', 'HTTP']
100
2. Beautiful Soup
解析へ
ページテーブルの構造は次のとおりです。
<table id="ip_list">
<tr>
<th class="country">国家</th>
<th>IP地址</th>
<th>端口</th>
<th>服务器地址</th>
<th class="country">是否匿名</th>
<th>类型</th>
<th class="country">速度</th>
<th class="country">连接时间</th>
<th width="8%">存活时间</th>
<th width="20%">验证时间</th>
</tr>
<tr class="odd">
<td class="country"><img src="//fs.xicidaili.com/images/flag/cn.png" alt="Cn" /></td>
<td>222.128.9.235</td>
<td>59593</td>
<td>
<a href="/2018-09-26/beijing">北京</a>
</td>
<td class="country">高匿</td>
<td>HTTPS</td>
<td class="country">
<div title="0.032秒" class="bar">
<div class="bar_inner fast" style="width:87%">
</div>
</div>
</td>
<td class="country">
<div title="0.006秒" class="bar">
<div class="bar_inner fast" style="width:97%">
</div>
</div>
</td>
<td>533天</td>
<td>20-03-13 15:21</td>
</tr>
...
まず、選択table
の下にあるすべてのtr
ラベルを:
from bs4 import BeautifulSoup
def get_detail(html):
soup = BeautifulSoup(html, 'lxml')
c1 = soup.select('#ip_list tr')
print(c1[1])
結果は以下の通りであります:
<tr class="odd">
<td class="country"><img alt="Cn" src="//fs.xicidaili.com/images/flag/cn.png"/></td>
<td>222.128.9.235</td>
<td>59593</td>
<td>
<a href="/2018-09-26/beijing">北京</a>
</td>
<td class="country">高匿</td>
<td>HTTPS</td>
<td class="country">
<div class="bar" title="0.032秒">
<div class="bar_inner fast" style="width:87%">
</div>
</div>
</td>
<td class="country">
<div class="bar" title="0.006秒">
<div class="bar_inner fast" style="width:97%">
</div>
</div>
</td>
<td>533天</td>
<td>20-03-13 15:21</td>
</tr>
次のステップは、それぞれのためのものであるtr
第二のタグ(ip
)、第三(ポート)と第(タイプ)td
のうち選択されたラベル。
from bs4 import BeautifulSoup
def get_detail(html):
soup = BeautifulSoup(html, 'lxml')
c1 = soup.select('#ip_list tr')
ls = []
for index, tr in enumerate(c1):
if index != 0:
td = tr.select('td')
ls.append({'proxies': td[1].string + ":" + td[2].string,
'types': td[5].string})
print(ls)
print(len(ls))
結果は以下の通りであります:
[{'proxies': '222.128.9.235:59593', 'types': 'HTTPS'}, {'proxies': '115.219.105.60:8010', 'types': 'HTTP'}, {'proxies': '117.88.177.204:3000', 'types': 'HTTP'}, {'proxies': '222.95.144.235:3000', 'types': 'HTTP'}, {'proxies': '59.42.88.110:8118', 'types': 'HTTPS'}, {'proxies': '118.181.226.166:44640', 'types': 'HTTP'}, {'proxies': '121.237.149.124:3000', 'types': 'HTTP'}, {'proxies': '218.86.200.26:8118', 'types': 'HTTPS'}, {'proxies': '106.6.138.18:8118', 'types': 'HTTP'}......]
100
ページ100データ、結果は正しいです。
3. pyquery
解析へ
pyquery
分析方法とBeautiful Soup
同様に、最初の表の最初の行を削除し、テーブル選択tr
タグ:
from pyquery import PyQuery as pq
def get_detail(html):
doc = pq(html)
doc('tr:first-child').remove() # 删除第一行
items = doc('#ip_list tr')
print(items)
出力から見ることができるitems
各項目の形式:
...
<tr class="">
<td class="country"><img src="//fs.xicidaili.com/images/flag/cn.png" alt="Cn"/></td>
<td>124.205.143.210</td>
<td>34874</td>
<td>
<a href="/2018-10-05/beijing">北京</a>
</td>
<td class="country">高匿</td>
<td>HTTPS</td>
<td class="country">
<div title="0.024秒" class="bar">
<div class="bar_inner fast" style="width:93%">
</div>
</div>
</td>
<td class="country">
<div title="0.004秒" class="bar">
<div class="bar_inner fast" style="width:99%">
</div>
</div>
</td>
<td>523天</td>
<td>20-03-12 02:20</td>
</tr>
...
次に、発電機によって取り出さそれぞれ、第2の選択td
タグ(ip
アドレス)、第三のtd
ラベル(ポート番号)、第6 td
タグ(タイプ)、辞書形式のリストを格納します。
from pyquery import PyQuery as pq
def get_detail(html):
doc = pq(html)
doc('tr:first-child').remove() # 删除第一行
items = doc('#ip_list tr')
ls = []
for i in items.items():
tmp1 = i('td:nth-child(2)') # 选取ip地址
tmp2 = i('td:nth-child(3)') # 选取端口
tmp3 = i('td:nth-child(6)') # 选取类型
ls.append({'proxies': tmp1.text() + ":" + tmp2.text(),
'types': tmp3.text()})
print(ls)
print(len(ls))
出力:
[{'proxies': '222.128.9.235:59593', 'types': 'HTTPS'}, {'proxies': '115.219.105.60:8010', 'types': 'HTTP'}, {'proxies': '117.88.177.204:3000', 'types': 'HTTP'}, {'proxies': '222.95.144.235:3000', 'types': 'HTTP'}, {'proxies': '59.42.88.110:8118', 'types': 'HTTPS'}, {'proxies': '118.181.226.166:44640', 'types': 'HTTP'}, {'proxies': '121.237.149.124:3000', 'types': 'HTTP'}, {'proxies': '218.86.200.26:8118', 'types': 'HTTPS'}......
100
100ページあたりの結果のデータが正確です。
(C)は、選択Baiduのサイトを得たプロキシフェッチをテストします
使用するのが困難あるいは不安定多くでそのフリーエージェントを取得するために登る、直接格納できないので、我々は、プロキシ要求にクロールできるかどうかをテストするサイトを選択する必要が成功すると、私が選んだのhttpを: //www.baidu.comテストとして、唯一成功した機関がデータベースに追加する話すことを要求します、要求は破棄以上の3倍の数を失敗しました。
このような薬剤の検出のために一般的にキューに入れられた要求が明確に不合理検出使用して、10秒またはそれより長いものを必要とし、それはライブラリ非同期要求を選択する必要があるaiohttp
コルーチンについて、非同期、参照することが可能でPythonの非同期コルーチンこの方法の使用は説明の上、aiohttp
ご覧ください、導入aiohttp中国の文書を。
二つの主なキーワードawait
とasync
、簡単に言えば、おそらくAプラススレッドで待機しているawait
修正し、この時点でこの場所にスレッドが乾燥を待っていられないだろうが、RANは、他のタスクBを実行します、オブジェクト応答するまで待機した後、すぐに戻ってきた下記の他のタスクを続行し、Bのタスクが一時的に棚上げ。しかし、await
後者の目的でなければならないcoroutine
オブジェクト、または返すことができるcoroutine
オブジェクト生成を、又は含む__await
(直接ではない理由であるメソッドによって返されるイテレータオブジェクトrequests
先行await
理由)。そして、我々は、機能を追加しasync
たオブジェクトとなり、変更後の機能のリターンをcoroutine
追加する「無脳」彼ができたので、ターゲットawait
にasync
ポートフォリオは、もちろん、あなたが追加した場合await
の場所を要求応答の種類を待つ必要はありませんか、データのアップロードとダウンロードを待っていますブロックされた状態に糸のようにどこへ行く場所は、それがどんな効果が再生されません、もちろん、それは間違っていないだろう。
以下のように検出剤が機能します:
# 测试代理
async def test_proxy(self, dic):
## 根据类型构造不同的代理及url
if dic["types"] == "HTTP":
test_url = "http://www.baidu.com/"
prop = "http://" + dic["proxies"]
else:
test_url = "https://www.baidu.com/"
prop = "https://" + dic["proxies"]
ua = UserAgent()
header = {'User-Agent': ua.random}
# 异步协程请求
async with aiohttp.ClientSession() as session:
while True:
try:
async with session.get(test_url, headers = header, proxy = prop, timeout = 15, verify_ssl=False) as resp:
if resp.status == 200:
self.success_test_count += 1
print(prop, "\033[5;36;40m===========>测试成功,写入数据库!=========%d次\033[;;m"%self.success_test_count)
await self.insert_to_mongo(dic) ## 调用写入mongodb数据库的函数
return
except Exception as e:
print(prop, "==测试失败,放弃==", e)
break
(D)記憶されたデータベースを選択
エージェント・プールを考慮した後、以下のようにさらに維持し、従って貯蔵のためのMongoDBを使用することを選択して、データを簡単に回避複製に挿入することができる場合、データベースストレージ機能します。
# 写入MongoDB数据库
async def insert_to_mongo(self, dic):
db = self.client.Myproxies
collection = db.proxies
collection.update_one(dic,{'$set': dic}, upsert=True) # 设置upsert=True,避免重复插入
print("\033[5;32;40m插入记录:" + json.dumps(dic), "\033[;;m")
(E)完全なコード
1.プロキシクロール舞台版
最後に、完全なコードは、これは、このマシンは、Iので、要求を行っているエージェントのプロキシバージョンの使用上のクロール段階である(次のされてip
私がしなければならなかったので、プロセスは、ポスティングデータを継続するために戻ってクロール、遅くなり、閉鎖されましたテストの代理バージョンを使用してプロキシなしのステージ、)、それはライブラリを解析することになると3の始まりを選択しlxml
、解析します:
import json
import time
import random
from fake_useragent import UserAgent
import asyncio
import aiohttp
# 避免出现RuntimeError错误
import nest_asyncio
nest_asyncio.apply()
from lxml import etree
import pymongo
class Get_prox:
def __init__(self):
# 初始化,连接MongoDB
self.client = pymongo.MongoClient('mongodb://localhost:27017/')
self.success_get_count = 0
self.success_test_count = 0
# 使用代理时,获取页面
async def get_page(self, session, url):
## 一个随机生成请求头的库
ua = UserAgent()
header = {'User-Agent': ua.random}
# 从本地文件获取代理池
proxies_pool = self.get_proxies()
while True:
try:
# 由于我一开始操作不慎ip被封禁了,因此在一开始抓取ip时我不得不使用了自己从
# 其他网站抓来的一批代理(如问题描述中所述),一共有5999条代理,每次随机选取一条
p = 'http://' + random.choice(proxies_pool)
async with session.get(url, headers = header, proxy = p, timeout = 10) as response:
await asyncio.sleep(2)
if response.status == 200:
self.success_get_count += 1
print("\033[5;36;40m----------------------请求成功-------------------%d次\033[;;m"%self.success_get_count)
return await response.text()
else:
print("\033[5;31;m", response.status, "\033[;;m")
continue
except Exception as e:
print("请求失败orz", e)
# 任务
async def get(self, url):
async with aiohttp.ClientSession() as session:
html = await self.get_page(session, url)
await self.get_detail(html)
# 测试代理
async def test_proxy(self, dic):
## 根据类型构造不同的代理及url
if dic["types"] == "HTTP":
test_url = "http://www.baidu.com/"
prop = "http://" + dic["proxies"]
else:
test_url = "https://www.baidu.com/"
prop = "https://" + dic["proxies"]
ua = UserAgent()
header = {'User-Agent': ua.random}
# 异步协程请求
async with aiohttp.ClientSession() as session:
while True:
try:
async with session.get(test_url, headers = header, proxy = prop, timeout = 15, verify_ssl=False) as resp:
if resp.status == 200:
self.success_test_count += 1
print(prop, "\033[5;36;40m===========>测试成功,写入数据库!=========%d次\033[;;m"%self.success_test_count)
await self.insert_to_mongo(dic) ## 调用写入mongodb数据库的函数
return
except Exception as e:
print(prop, "==测试失败,放弃==", e)
break
# 获取代理池
def get_proxies(self):
with open("proxies.txt", "r") as f:
ls = json.loads(f.read())
return ls
# 使用lxml爬取
async def get_detail(self, html):
html = etree.HTML(html)
dic = {}
ip = html.xpath('//tr[@class="odd" or @class=""]/td[2]/text()')
port = html.xpath('//tr[@class="odd" or @class=""]/td[3]/text()')
types = html.xpath('//tr[@class="odd" or @class=""]/td[6]/text()')
for i in range(len(ip)):
dic['proxies'] = ip[i] + ":" + port[i]
dic['types'] = types[i]
await self.test_proxy(dic)
# 写入MongoDB数据库
async def insert_to_mongo(self, dic):
db = self.client.Myproxies
collection = db.proxies
collection.update_one(dic,{'$set': dic}, upsert=True) # 设置upsert=True,避免重复插入
print("\033[5;32;40m插入记录:" + json.dumps(dic), "\033[;;m")
# 主线程
if __name__ == "__main__":
urls = []
start = time.time()
# 抓取前10页数据
for i in range(1, 11):
urls.append("http://www.xicidaili.com/nn/" + str(i))
c = Get_prox()
# 创建10个未来任务对象
tasks = [asyncio.ensure_future(c.get(url)) for url in urls]
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))
end = time.time()
total = (end - start)/60.0
print("完成,总耗时:", total, "分钟!")
:実装プロセスは、次のようにログの一部、ログの多くが印刷されます
かどうか要求プロセスやテストプロセスを、エージェントがip
完全に終了し、実行は47分が終了した後の表示に時間がかかりにいくつかの時間を必要とする、成功率が非常に低い要求します。
ログを簡単に見ては、最後のデータが正常に第八を挿入されている参照してください。。。。。
データベースは、私は、データベース内のデータを実行した後、数回繰り返され、以上見て、それが唯一の50に挿入されます。
エージェントの2クロールステージバージョンが使用されていません
プロキシデータを使用せずにクロール段階の掲示版、すなわちの使用続行をrequests
クロールして、テストをaiohttp
スクリーニングエージェント・プロセスの第一段階の待ち時間をなくし、。
import json
import time
import requests
from fake_useragent import UserAgent
import asyncio
import aiohttp
# 避免出现RuntimeError错误
import nest_asyncio
nest_asyncio.apply()
from lxml import etree
import pymongo
class Get_prox:
def __init__(self):
# 初始化,连接MongoDB
self.client = pymongo.MongoClient('mongodb://localhost:27017/')
self.success_get_count = 0
self.success_test_count = 0
# 不使用代理时,获取页面
def get_page(self, url):
## 一个随机生成请求头的库
ua = UserAgent()
header = {'User-Agent': ua.random}
while True:
try:
response = requests.get(url, headers = header, timeout = 10)
time.sleep(1.5)
if response.status_code == 200:
self.success_get_count += 1
print("\033[5;36;40m----------------------请求成功-------------------%d次\033[;;m"%self.success_get_count)
return response.text
else:
print("\033[5;31;m", response.status_code, "\033[;;m")
continue
except Exception as e:
print("请求失败orz", e)
# 任务
def get(self, urls):
htmls = []
# 先将抓取的页面都存入列表中
for url in urls:
htmls.append(self.get_page(url))
# 测试代理使用异步
tasks = [asyncio.ensure_future(self.get_detail(html)) for html in htmls]
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))
# 测试代理
async def test_proxy(self, dic):
## 根据类型构造不同的代理及url
if dic["types"] == "HTTP":
test_url = "http://www.baidu.com/"
prop = "http://" + dic["proxies"]
else:
test_url = "https://www.baidu.com/"
prop = "https://" + dic["proxies"]
ua = UserAgent()
header = {'User-Agent': ua.random}
# 异步协程请求
async with aiohttp.ClientSession() as session:
while True:
try:
async with session.get(test_url, headers = header, proxy = prop, timeout = 15, verify_ssl=False) as resp:
if resp.status == 200:
self.success_test_count += 1
print(prop, "\033[5;36;40m===========>测试成功,写入数据库!=========%d次\033[;;m"%self.success_test_count)
await self.insert_to_mongo(dic) ## 调用写入mongodb数据库的函数
return
except Exception as e:
print(prop, "==测试失败,放弃==", e)
break
# 使用lxml爬取
async def get_detail(self, html):
html = etree.HTML(html)
dic = {}
ip = html.xpath('//tr[@class="odd" or @class=""]/td[2]/text()')
port = html.xpath('//tr[@class="odd" or @class=""]/td[3]/text()')
types = html.xpath('//tr[@class="odd" or @class=""]/td[6]/text()')
for i in range(len(ip)):
dic['proxies'] = ip[i] + ":" + port[i]
dic['types'] = types[i]
await self.test_proxy(dic)
# 写入MongoDB数据库
async def insert_to_mongo(self, dic):
db = self.client.Myproxies
collection = db.proxies
collection.update_one(dic,{'$set': dic}, upsert=True) # 设置upsert=True,避免重复插入
print("\033[5;32;40m插入记录:" + json.dumps(dic) + "\033[;;m")
# 主线程
if __name__ == "__main__":
urls = []
start = time.time()
# 抓取前10页数据
for i in range(1, 11):
urls.append("http://www.xicidaili.com/nn/" + str(i))
c = Get_prox()
c.get(urls)
end = time.time()
total = (end - start)/60.0
print("完成,总耗时:", total, "分钟!")
次のように他の小さなパートナーで測定した結果ショットは、次のとおり
、ステージをクロール10個の要求が非常にスムーズに進みました。
最後に、合計時間19分には、エージェントをスクリーニングし、ステージをクロールの前には見えないが、本当に自由な時間を大幅に節約することができます!
IV。問題と解決策
(A)ip
のアドレスが禁止されています
使用開始以来lxml
スリープ時間を設定するために、解析するルールを探索するためのライブラリを解析する際には便利ではない、と後で過失に、大手のクロールページを数回クロールした後、結果をスリープ時間を設定することを忘れて、ログを見つけました次のように出力情報の内容は次のようになります。
{"proxies": "121.237.148.195:3000", "types": "HTTP"}
{"proxies": "121.234.31.44:8118", "types": "HTTPS"}
{"proxies": "117.88.4.63:3000", "types": "HTTP"}
{"proxies": "222.95.144.58:3000", "types": "HTTP"}
发生错误: 'NoneType' object has no attribute 'xpath'
发生错误: 'NoneType' object has no attribute 'xpath'
发生错误: 'NoneType' object has no attribute 'xpath'
发生错误: 'NoneType' object has no attribute 'xpath'
发生错误: 'NoneType' object has no attribute 'xpath'
发生错误: 'NoneType' object has no attribute 'xpath'
......
次の結果が表示されますが、印刷応答ステータスコードを取得するためのプログラムの終了後:
503
503
503
503
503
...
私はまた、あまりにも多くの時間をクロールし、私のIPが禁止されているWebページに描画することができ、サイトにブラウザを介して入力することはできません。
- ソリューション
最初に私にいくつかの選択したサイトを介したIP、IP、他のフリーエージェントで直接選択したが、非常に大きな割合を持っているIPの無料プロキシが環境中の既存のプロジェクトのプロキシIPプールを構築するために、インターネットの使用を使用していない発見されたと私はまっすぐに行ったような構成では、時間のかかるに依存している66のフリーエージェントネットワーク:6000プロキシIPを抽出するために、サイトの無料IP抽出機能を使用して、
直接、6000プロキシ情報をクリック抽出が含まれているページから、その後、あなたは、ページ6000(実際に撮影し5999)、ローカルファイルへのプロキシ情報をクロールするために、このダイレクトを生成するための簡単なプログラムを書くことができます。
response1 = requests.get("http://www.66ip.cn/mo.php?sxb=&tqsl=6000&port=&export=&ktip=&sxa=&submit=%CC%E1++%C8%A1&textarea=")
html = response1.text
print(response1.status_code == 200)
pattern = re.compile("br />(.*?)<", re.S)
items = re.findall(pattern, html)
for i in range(len(items)):
items[i] = items[i].strip()
print(len(items))
with open("proxies.txt", "w") as f:
f.write(json.dumps(items))
そして、クローラ・エージェント・プールとしてこのファイルをお読みください。
# 获取代理池
def get_proxies(self):
with open("proxies.txt", "r") as f:
ls = json.loads(f.read())
return ls
その後、各要求がランダムからプロキシエージェント・プールを選択します。
def get_page(ls):
url = []
ua = UserAgent()
with open("proxies.txt", "r") as f:
ls = json.loads(f.read())
for i in range(1, page+1):
url.append("http://www.xicidaili.com/nn/" + str(i))
count = 1
errcount = 1
for u in url:
while True:
try:
header = {'User-Agent': ua.random}
handler = {'http': 'http://' + random.choice(ls)}
response = requests.get(u, headers = header, proxies = handler, timeout = 10)
time.sleep(1)
get_detail(response.text)
if response.status_code == 200:
print("选取ip:", handler, "请求成功---------------------------第%d次"%count)
count += 1
else:
continue
break
except:
print("选取ip:", handler, ", 第%d请求发生错误"%errcount)
errcount += 1
しかし、使用することは困難で、それぞれの試行結果としては、数秒の時間を要するタスクのみに責任があるが、多くのIPプロキシが存在することができますスレッドをスケジュールするときに問題がある持っているが、ほとんどのケースではありませんエラーが発生していることを要求します。
私は、非同期リクエスト・ライブラリを使用することを選択したように、この問題を解決するために、我々は、スケジューリングクロールページへのシングルスレッドのシングルステップのアプローチを使用するように選択することはできませんaiohttp
。
参考記事Pythonの非同期コルーチンの導入、使用するとaiohttp中国の文書は、私は、各ことを、非同期コルーチンスケジューラを実装するために10個のタスク(タスク10ページをクロール)とコルーチンオブジェクトを作成することを学びましたスレッドが要求されたタスクを待たずに、要求に遭遇し、次のタスクをスケジュールすることができたときに10個の要求が成功したとき、我々は次の関数呼び出しを入力することができますので、合計時間の消費量は約10倍に低減することができ、 (全ての機能が表示されていない)は、以下の方法です。
# 使用代理时,获取页面
async def get_page(self, session, url):
## 一个随机生成请求头的库
ua = UserAgent()
header = {'User-Agent': ua.random}
# 从本地文件获取代理池
proxies_pool = self.get_proxies()
while True:
try:
# 由于我一开始操作不慎ip被封禁了,因此在一开始抓取ip时我不得不使用了自己从
# 其他网站抓来的一批代理(如问题描述中所述),一共有5999条代理,每次随机选取一条
p = 'http://' + random.choice(proxies_pool)
async with session.get(url, headers = header, proxy = p, timeout = 10) as response:
await asyncio.sleep(2)
if response.status == 200:
self.success_get_count += 1
print("\033[5;36;40m----------------------请求成功-------------------%d次\033[;;m"%self.success_get_count)
return await response.text()
else:
print("\033[5;31;m", response.status, "\033[;;m")
continue
except Exception as e:
print("请求失败orz", e)
# 任务
async def get(self, url):
async with aiohttp.ClientSession() as session:
html = await self.get_page(session, url)
await self.get_detail(html)
# 主线程
if __name__ == "__main__":
urls = []
start = time.time()
# 抓取前10页数据
for i in range(1, 11):
urls.append("http://www.xicidaili.com/nn/" + str(i))
c = Get_prox()
# 创建10个未来任务对象
tasks = [asyncio.ensure_future(c.get(url)) for url in urls]
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))
end = time.time()
total = (end - start)/60.0
print("完成,总耗时:", total, "分钟!")
印刷された雑誌の一部としてプロセスをクロール、目に見える、成功の要求エージェント確率が非常に低い、次のステップは待つことです。
(B)非同期動作のエラーRuntimeError
エラー
コルーチンは非同期でプログラムを実行開始すると、エラーログコンソール出力は次のとおりです。
RuntimeError: asyncio.run() cannot be called from a running event loop
インターネット検索ソリューションは、プログラムの先頭に追加します:
import nest_asyncio
nest_asyncio.apply()
後にエラーがなく、特定の理由は不明。
改善のためのV.さらにエリア
- ない場合のために
ip
、ステージ機関をクロール禁止されている直接スリープ時間を設定するために注意を払う、同じよう要請します。実際には、エージェントをクロールする際に同時にサイトをクロール異なるエージェントの数、上のようにあなたは、非同期リクエストと一緒にメカニズムを置くことができることもでてきたことができます。例えば、各タスクは別に、イベントループ非同期タスクコルーチン、外出先に追加されたウェブサイトの要求のための異なる要求を使用して、複数のタスクを作成します。 - 私のアプローチがあり、プールのプロジェクトを動的に維持されているネットワーク上の多くのエージェントがあり、静的であり、ローカルデータベースへの唯一の薬剤である
web
インターフェイス、api
インターフェイスは、だけでなく、より複雑な実装は、フォローアップすることで、深さはさらにすることができ学習。