Что такое рептилия?
Если вы никогда не контактировали с рептилиями, вы можете немного запутаться, что такое рептилия? На самом деле концепция краулера очень проста.В эпоху Интернета всемирная паутина уже является носителем большого количества информации.Как эффективно использовать и извлекать эту информацию является огромной проблемой.Когда мы отправляем запрос на веб-сайт с помощью браузера, сервер отвечаетHTML
Текст обрабатывается и отображается браузером. Сканер использует это, имитируя запрос пользователя через программу, чтобы получитьHTML
содержание и извлечь из него необходимые данные и информацию. Если вы думаете о сети как о паутине, поисковые программы подобны паукам в паутине, постоянно сканирующим данные и информацию.
Концепция рептилий очень проста и понятна, используяpython
Встроенныйurllib
Любая библиотека может реализовать простой сканер. Следующий код является очень простым сканером, если есть базовыеpython
Знания должны быть поняты. он собирает все<a>
теги (без вынесения каких-либо правил), а затем продолжить глубокий поиск по этим ссылкам.
from bs4 import BeautifulSoup
import urllib
import os
from datetime import datetime
# 网页的实体类,只含有两个属性,url和标题
class Page(object):
def __init__(self,url,title):
self._url = url
self._title = title
def __str__(self):
return '[Url]: %s [Title]: %s' %(self._url,self._title)
__repr__ = __str__
@property
def url(self):
return self._url
@property
def title(self):
return self._title
@url.setter
def url(self,value):
if not isinstance(value,str):
raise ValueError('url must be a string!')
if value == '':
raise ValueError('url must be not empty!')
self._url = value
@title.setter
def title(self,value):
if not isinstance(value,str):
raise ValueError('title must be a string!')
if value == '':
raise ValueError('title must be not empty!')
self._title = value
class Spider(object):
def __init__(self,init_page):
self._init_page = init_page # 种子网页,也就是爬虫的入口
self._pages = []
self._soup = None # BeautifulSoup 一个用来解析HTML的解析器
def crawl(self):
start_time = datetime.now()
print('[Start Time]: %s' % start_time)
start_timestamp = start_time.timestamp()
tocrawl = [self._init_page] # 记录将要爬取的网页
crawled = [] # 记录已经爬取过的网页
# 不断循环,直到将这张图搜索完毕
while tocrawl:
page = tocrawl.pop()
if page not in crawled:
self._init_soup(page)
self._packaging_to_pages(page)
links = self._extract_links()
self._union_list(tocrawl,links)
crawled.append(page)
self._write_to_curdir()
end_time = datetime.now()
print('[End Time]: %s' % end_time)
end_timestamp = end_time.timestamp()
print('[Total Time Consuming]: %f.3s' % (start_timestamp - end_timestamp) / 1000)
def _init_soup(self,page):
page_content = None
try:
# urllib可以模拟用户请求,获得响应的HTML文本内容
page_content = urllib.request.urlopen(page).read()
except:
page_content = ''
# 初始化BeautifulSoup,参数二是使用到的解析器名字
self._soup = BeautifulSoup(page_content,'lxml')
def _extract_links(self):
a_tags = self._soup.find_all('a') # 找到所有a标签
links = []
# 收集所有a标签中的链接
for a_tag in a_tags:
links.append(a_tag.get('href'))
return links
def _packaging_to_pages(self,page):
title_string = ''
try:
title_string = self._soup.title.string # 获得title标签中的文本内容
except AttributeError as e :
print(e)
page_obj = Page(page,title_string)
print(page_obj)
self._pages.append(page_obj)
# 将爬取到的所有信息写入到当前目录下的out.txt文件
def _write_to_curdir(self):
cur_path = os.path.join(os.path.abspath('.'),'out.txt')
print('Start write to %s' % cur_path)
with open(cur_path,'w') as f:
f.write(self._pages)
# 将dest中的不存在于src的元素合并到src
def _union_list(self,src,dest):
for dest_val in dest:
if dest_val not in src:
src.append(dest_val)
@property
def init_page(self):
return self._init_page
@property
def pages(self):
return self._pages
def test():
spider = Spider('https://sylvanassun.github.io/')
spider.crawl()
if __name__ == '__main__':
test()
Однако, если мы хотим внедрить высокопроизводительный искатель, требуемая сложность также возрастет.Эта статья нацелена на быстрое внедрение, поэтому нам нужно использовать структуру искателя, реализованную другими, в качестве основы для создания нашего искателя изображений (если у вас есть время, конечно, я призываю вас построить свои собственные колеса).
Автор этой статьи:SylvanasSun(sylvanas.sun@gmail.com).Для перепечатки следующий абзац следует разместить в начале статьи (сохранить гиперссылку).
Эта статья была впервые опубликована сSylvanasSun Blog, оригинальная ссылка:sylva, то есть sun.GitHub.IO/2017/09/20/…
BeautifulSoup
BeautifulSoup — этоHTML
иXML
извлекать данные изpython
библиотека. Beautiful Soup автоматически конвертирует входные документы в кодировку Unicode, а выходные документы — в кодировку utf-8. Вам не нужно учитывать метод кодирования, если только в документе не указан метод кодирования, тогда Beautiful Soup не сможет автоматически распознать метод кодирования. Затем вам просто нужно указать исходную кодировку.
Правильное использование BeautifulSoup может сэкономить нам много времени при написании регулярных выражений.Если вам нужен более точный поиск, BeautifulSoup также поддерживает запросы с использованием регулярных выражений.
BeautifulSoup3 прекратил техническое обслуживание, и теперь мы в основном используем BeautifulSoup4.Установить BeautifulSoup4 очень просто, просто выполните следующие команды.
pip install beautifulsoup4
затем изbs4
Импортируйте объект BeautifulSoup в модуль и создайте этот объект.
from bs4 import BeautifulSoup
soup = BeautifulSoup(body,'lxml')
Создание объекта BeautifulSoup требует передачи двух параметров, первый из которых необходимо проанализировать.HTML
content, вторым параметром является имя парсера (если этот параметр не передан, BeautifulSoup будет использовать его по умолчаниюpython
встроенный парсерhtml.parser
). BeautifulSoup поддерживает множество парсеров, в том числеlxml
,html5lib
,html.parser
.
Пользователь должен установить сторонний синтаксический анализатор, который используется в этой статье.lxml
Parser, команда установки выглядит следующим образом (она также должна сначала установить библиотеку языка C).
pip install lxml
Ниже приведен пример, демонстрирующий базовый способ использования BeautifulSoup.Если вы хотите узнать больше, вы можете обратиться кДокументация BeautifulSoup.
from bs4 import BeautifulSoup
html = """
<html><head><title>The Dormouse's story</title></head>
<body>
<p class="title" name="dromouse"><b>The Dormouse's story</b></p>
<p class="story">Once upon a time there were three little sisters; and their names were
<a href="http://example.com/elsie" class="sister" id="link1"><!-- Elsie --></a>,
<a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> and
<a href="http://example.com/tillie" class="sister" id="link3">Tillie</a>;
and they lived at the bottom of a well.</p>
<p class="story">...</p>
"""
soup = BeautifulSoup(html,'lxml')
# 格式化输出soup中的内容
print(soup.prettify())
# 可以通过.操作符来访问标签对象
title = soup.title
print(title)
p = soup.p
print(p)
# 获得title标签中的文本内容,这2个方法得到的结果是一样的
print(title.text)
print(title.get_text())
# 获得head标签的所有子节点,contents返回的是一个列表,children返回的是一个迭代器
head = soup.head
print(head.contents)
print(head.children)
# 获得所有a标签,并输出每个a标签href属性中的内容
a_tags = soup.find_all('a')
for a_tag in a_tags:
print(a_tag['href'])
# find函数与find_all一样,只不过返回的是找到的第一个标签
print(soup.find('a')['href'])
# 根据属性查找,这2个方法得到的结果是一样的
print(soup.find('p',class_='title'))
print(soup.find('p',attrs={'class': 'title'}))
Scrapy
Scrapy
Это мощная платформа сканера, в которой реализована высокопроизводительная структура сканера и предоставляется программистам множество настроек для настройки. использоватьScrapy
Просто напишите логику нашего сканера на его правилах.
Сначала нужно установитьScrapy
,Выполнение заказаpip install scrapy
. а затем выполнить командуscrapy startproject 你的项目名
генерироватьScrapy
базовая папка проекта. В результате структура проекта выглядит следующим образом.
你的项目名/
scrapy.cfg
你的项目名/
__init__.py
items.py
pipelines.py
settings.py
spiders/
__init__.py
...
-
scrapy.cfg
: файл конфигурации для проекта. -
items.py
: модуль элемента, пользователь должен определить класс сущности инкапсуляции данных в этом модуле. -
pipelines.py
: модуль конвейера, пользователь должен определить логику обработки данных в этом модуле (например, сохранение в базе данных и т. д.). -
settings.py
: Этот модуль определяет различные переменные конфигурации по всему проекту. -
spiders/
: определите собственный модуль сканера пользователя в этом пакете.
запускатьScrapy
Краулер тоже очень простой, достаточно выполнить командуscrapy crawl 你的爬虫名
. Представьте нижеScrapy
Демонстрации ключевых модулей вScrapy
Для получения дополнительной информации см.Официальная документация Scrapy.
items
items
Модуль в основном предназначен для инкапсуляции просканированных неструктурированных данных в структурированный объект, пользовательскийitem
Класс должен наследовать отscrapy.Item
, и каждому свойству должно быть присвоено значениеscrapy.Field()
.
import scrapy
class Product(scrapy.Item):
name = scrapy.Field()
price = scrapy.Field()
stock = scrapy.Field()
действоватьitem
объект подобен манипулированиюdict
так же просто, как объект.
product = Product()
# 对属性赋值
product['name'] = 'Sylvanas'
product['price'] = 998
# 获得属性
print(product['name'])
print(product['price'])
pipelines
когдаItem
Он прибудет после того, как будет упакован сканеромPipeline
класс, вы можете определить свой собственныйPipeline
класс, чтобы решить, кто будетItem
стратегия обработки.
каждыйPipeline
Могут быть реализованы следующие функции.
-
process_item(item, spider)
: каждыйPipeline
вызовет эту функцию для обработкиItem
, функция должна возвращатьItem
, если при обработке возникнет ошибка, то ее можно будет выкинутьDropItem
аномальный. -
open_spider(spider)
: когдаspider
Эта функция будет вызываться в начале, и вы можете использовать эту функцию для открытия файлов и других операций. -
close_spider(spider)
:когдаspider
Эта функция будет вызываться при закрытии, вы можете использовать эту функцию дляIO
ресурсы закрыты. -
from_crawler(cls, crawler)
: Эта функция используется для полученияsettings.py
свойства в модуле. Обратите внимание, что эта функция является методом класса.
from scrapy.exceptions import DropItem
class PricePipeline(object):
vat_factor = 1.15
def __init__(self, HELLO):
self.HELLO = HELLO
def process_item(self, item, spider):
if item['price']:
if item['price_excludes_vat']:
item['price'] = item['price'] * self.vat_factor
return item
else:
raise DropItem("Missing price in %s" % item)
@classmethod
def from_crawler(cls, crawler):
settings = crawler.settings # 从crawler中获得settings
return cls(settings['HELLO']) # 返回settings中的属性,将由__init__函数接收
когда вы определяете свойPipeline
После этого также необходимоsettings.py
тебеPipeline
Сделайте настройки.
ITEM_PIPELINES = {
# 后面跟的数字是优先级别
'pipeline类的全路径': 300,
}
spiders
существуетspiders
модуль, пользователь может настроитьSpider
class, чтобы сформулировать собственную логику сканера и стратегию инкапсуляции данных.каждыйSpider
должен наследовать отclass scrapy.spider.Spider
,ЭтоScrapy
Самый простой базовый класс сканера в , он не имеет специальных функций,Scrapy
Также предоставляет другие функции с различнымиSpider
Классы доступны пользователям на выбор, поэтому я не буду здесь вдаваться в подробности, вы можете обратиться к официальной документации.
Пользователи могут настраивать конфигурацию с помощью следующих свойств.Spider
:
-
name
: ЭтоSpider
Название,Scrapy
Необходимо найти по этому свойствуSpider
и запустите краулер, он уникален и обязателен. -
allowed_domains
: это свойство указываетSpider
Доменные имена, которые разрешены для обхода. -
start_urls
:Spider
Список веб-страниц, которые будут сканироваться при запуске. -
start_requests()
: ФункцияSpider
Функция запускается при начале сканирования. Она будет вызываться только один раз. Некоторые веб-сайты должны требовать от пользователей входа в систему. Вы можете использовать эту функцию для имитации входа в систему. -
make_requests_from_url(url)
: Функция получаетurl
и вернутьсяRequest
объект. Если функция не переопределена, по умолчанию используетсяparse(response)
функционировать как функция обратного вызова и включатьdont_filter
параметр (этот параметр используется для фильтрации дубликатовurl
из). -
parse(response)
: Когда запрос не устанавливает функцию обратного вызова, она будет вызываться по умолчанию.parse(response)
. -
log(message[, level, component])
: для регистрации. -
closed(reason)
: когдаSpider
Вызывается при закрытии.
import scrapy
class MySpider(scrapy.Spider):
name = 'example.com'
allowed_domains = ['example.com']
start_urls = [
'http://www.example.com/1.html',
'http://www.example.com/2.html',
'http://www.example.com/3.html',
]
def parse(self, response):
self.log('A response from %s just arrived!' % response.url)
Другие зависимые библиотеки
Requests
Requests
также третье лицоpython
библиотека, это лучше, чемpython
Встроенныйurllib
Проще и удобнее в использовании. Просто установите (pip install requests
), а после импорта пакета вы можете легко инициировать запрос на веб-сайт.
import requests
# 支持http的各种类型请求
r = requests.post("http://httpbin.org/post")
r = requests.put("http://httpbin.org/put")
r = requests.delete("http://httpbin.org/delete")
r = requests.head("http://httpbin.org/get")
r = requests.options("http://httpbin.org/get")
# 获得响应内容
r.text # 返回文本
r.content # 返回字节
r.raw # 返回原始内容
r.json() # 返回json
Дополнительные параметры и содержание см.ЗапросыДокументация.
BloomFilter
BloomFilter
это структура данных, используемая для фильтрации повторяющихся данных, мы можем использовать ее дляurl
фильтровать. используется в этой статьеBloomFilter
Отpython-bloomfilter, другие пользователи операционной системы, пожалуйста, используйтеpip install pybloom
Командная установка, пользователи Windows, пожалуйста, используйтеpip install pybloom-live
(Оригинал не подходит для Windows).
анализировать
После введения необходимых библиотек зависимостей мы, наконец, можем приступить к реализации нашего собственного сканера изображений. Наша цель - поднятьсяhttps://www.deviantart.com/
Прежде чем писать программу-краулер, вам необходимо проанализировать страницы изображений на веб-сайте.HTML
структура, так что исходный адрес изображения может быть найден целенаправленным образом.
Дабы удостовериться в качестве просканированных картинок, я решил начать обход с популярных страниц, ссылкаhttps://www.deviantart.com/whats-hot/
.
После открытия инструментов разработчика браузера вы можете обнаружить, что каждое изображение создаетсяa
этикетки, каждаяa
помеченclass
заtorpedo-thumb-link
, и этоa
помеченhref
Это страница подробностей этой картинки (если мы начнем сканировать картинки отсюда, то мы будем сканировать только миниатюры).
После входа на страницу сведений не сканируйте сразу исходный адрес текущей картинки, потому что картинка, отображаемая на текущей странице, не в исходном формате, мы дважды щелкаем картинку, чтобы увеличить ее, а затем используем инструменты разработчика для захвата расположение картины.img
После тега позвольте сканеру получить исходный адрес в теге.
После получения исходного адреса изображения моя стратегия состоит в том, чтобы позволить сканеру продолжить сканирование других изображений, рекомендованных на странице.С помощью инструментов разработчика можно обнаружить, что эти изображения инкапсулированы вclass
заtt-crop thumb
изdiv
тег, и первыйa
Вложенный тег — это просто ссылка на страницу сведений об изображении.
Начальная конфигурация
в сетиHTML
После анализа можно приступать к написанию программы, сначала использоватьScrapy
Команда для инициализации проекта. позжеsettings.py
Выполните следующую настройку.
# 这个是网络爬虫协议,爬虫访问网站时都会检查是否有robots.txt文件,
# 然后根据文件中的内容选择性地进行爬取,我们这里设置为False即不检查robots.txt
ROBOTSTXT_OBEY = False
# 图片下载的根目录路径
IMAGES_STORE = '.'
# 图片最大下载数量,当下载的图片达到这个数字时,将会手动关闭爬虫
MAXIMUM_IMAGE_NUMBER = 10000
затем определите нашItem
.
import scrapy
class DeviantArtSpiderItem(scrapy.Item):
author = scrapy.Field() # 作者名
image_name = scrapy.Field() # 图片名
image_id = scrapy.Field() # 图片id
image_src = scrapy.Field() # 图片的源地址
создай свой собственныйspider
модуль сSpider
своего рода.
import requests
from bs4 import BeautifulSoup
# this import package is right,if PyCharm give out warning please ignore
from deviant_art_spider.items import DeviantArtSpiderItem
from pybloom_live import BloomFilter
from scrapy.contrib.linkextractors.lxmlhtml import LxmlLinkExtractor
from scrapy.contrib.spiders import CrawlSpider, Rule
from scrapy.http import Request
class DeviantArtImageSpider(CrawlSpider):
name = 'deviant_art_image_spider'
# 我不想让scrapy帮助过滤所以设置为空
allowed_domains = ''
start_urls = ['https://www.deviantart.com/whats-hot/']
rules = (
Rule(LxmlLinkExtractor(
allow={'https://www.deviantart.com/whats-hot/[\?\w+=\d+]*', }),
callback='parse_page', # 设置回调函数
follow=True # 允许爬虫不断地跟随链接进行爬取
),
)
headers = {
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36"
" (KHTML, like Gecko) Chrome/38.0.2125.111 Safari/537.36",
"Referer": "https://www.deviantart.com/"
}
# 初始化BloomFilter
filter = BloomFilter(capacity=15000)
DeviantArtImageSpider
унаследовано отCrawlSpider
, классScrapy
чаще всего используетсяSpider
класс, который проходитRule
класс для определения правил обхода ссылок, в приведенном выше коде используются регулярные выраженияhttps://www.deviantart.com/whats-hot/[\?\w+=\d+]*
, это регулярное выражение будет посещать первые страницы для каждой страницы.
Парсинг популярных страниц
Когда краулер запустится, он сначала посетит популярную страницу, а после ответа на запрос будет вызвана функция обратного вызова, в которой нам необходимо получить данные, полученные при анализе выше.<a class = 'torpedo-thumb-link'>
тег, а затем извлеките ссылку на страницу сведений о каждом изображении.
def parse_page(self, response):
soup = self._init_soup(response, '[PREPARING PARSE PAGE]')
if soup is None:
return None
# 找到所有class为torpedo-thumb-link的a标签
all_a_tag = soup.find_all('a', class_='torpedo-thumb-link')
if all_a_tag is not None and len(all_a_tag) > 0:
for a_tag in all_a_tag:
# 提取图片详情页,然后对详情页链接发起请求,并设置回调函数
detail_link = a_tag['href']
request = Request(
url=detail_link,
headers=self.headers,
callback=self.parse_detail_page
)
# 通过request与response对象来传递Item
request.meta['item'] = DeviantArtSpiderItem()
yield request
else:
self.logger.debug('[PARSE FAILED] get <a> tag failed')
return None
# 初始化BeautifulSoup对象
def _init_soup(self, response, log):
url = response.url
self.headers['Referer'] = url
self.logger.debug(log + ' ' + url)
body = requests.get(url, headers=self.headers, timeout=2).content
soup = BeautifulSoup(body, 'lxml')
if soup is None:
self.logger.debug('[PARSE FAILED] read %s body failed' % url)
return None
return soup
Разобрать страницу сведений
parse_page()
Функция будет постоянно отправлять запросы на ссылку страницы сведений, а функция обратного вызова для анализа страницы сведений должна обрабатывать данные и инкапсулировать их вItem
, вам также необходимо извлечь ссылку на дополнительные изображения на странице сведений и отправить запрос.
def parse_detail_page(self, response):
if response.url in self.filter:
self.logger.debug('[REPETITION] already parse url %s ' % response.url)
return None
soup = self._init_soup(response, '[PREPARING DETAIL PAGE]')
if soup is None:
return None
# 包装Item并返回
yield self.packing_item(response.meta['item'], soup)
self.filter.add(response.url)
# 继续抓取当前页中的其他图片
all_div_tag = soup.find_all('div', class_='tt-crop thumb')
if all_div_tag is not None and len(all_div_tag) > 0:
for div_tag in all_div_tag:
detail_link = div_tag.find('a')['href']
request = Request(
url=detail_link,
headers=self.headers,
callback=self.parse_detail_page
)
request.meta['item'] = DeviantArtSpiderItem()
yield request
else:
self.logger.debug('[PARSE FAILED] get <div> tag failed')
return None
# 封装数据到Item
def packing_item(self, item, soup):
self.logger.debug('[PREPARING PACKING ITEM]..........')
img = soup.find('img', class_='dev-content-full')
img_alt = img['alt'] # alt属性中保存了图片名与作者名
item['image_name'] = img_alt[:img_alt.find('by') - 1]
item['author'] = img_alt[img_alt.find('by') + 2:]
item['image_id'] = img['data-embed-id'] # data-embed-id属性保存了图片id
item['image_src'] = img['src']
self.logger.debug('[PACKING ITEM FINISHED] %s ' % item)
return item
Обращение с предметами
заItem
Обработка заключается в том, чтобы просто назвать и загрузить изображение на локальный. Я не использую многопроцессорность или многопоточность, а также не используюScrapy
автономныйImagePipeline
(Степень свободы невелика), и заинтересованная детская обувь может реализовать ее самостоятельно.
import requests
import threading
import os
from scrapy.exceptions import DropItem, CloseSpider
class DeviantArtSpiderPipeline(object):
def __init__(self, IMAGE_STORE, MAXIMUM_IMAGE_NUMBER):
if IMAGE_STORE is None or MAXIMUM_IMAGE_NUMBER is None:
raise CloseSpider('Pipeline load settings failed')
self.IMAGE_STORE = IMAGE_STORE
self.MAXIMUM_IMAGE_NUMBER = MAXIMUM_IMAGE_NUMBER
# 记录当前下载的图片数量
self.image_max_counter = 0
# 根据图片数量创建文件夹,每1000张在一个文件夹中
self.dir_counter = 0
def process_item(self, item, spider):
if item is None:
raise DropItem('Item is null')
dir_path = self.make_dir()
# 拼接图片名称
image_final_name = item['image_name'] + '-' + item['image_id'] + '-by@' + item['author'] + '.jpg'
dest_path = os.path.join(dir_path, image_final_name)
self.download_image(item['image_src'], dest_path)
self.image_max_counter += 1
if self.image_max_counter >= self.MAXIMUM_IMAGE_NUMBER:
raise CloseSpider('Current downloaded image already equal maximum number')
return item
def make_dir(self):
print('[IMAGE_CURRENT NUMBER] %d ' % self.image_max_counter)
if self.image_max_counter % 1000 == 0:
self.dir_counter += 1
path = os.path.abspath(self.IMAGE_STORE)
path = os.path.join(path, 'crawl_images')
path = os.path.join(path, 'dir-' + str(self.dir_counter))
if not os.path.exists(path):
os.makedirs(path)
print('[CREATED DIR] %s ' % path)
return path
def download_image(self, src, dest):
print('[Thread %s] preparing download image.....' % threading.current_thread().name)
response = requests.get(src, timeout=2)
if response.status_code == 200:
with open(dest, 'wb') as f:
f.write(response.content)
print('[DOWNLOAD FINISHED] from %s to %s ' % (src, dest))
else:
raise DropItem('[Thread %s] request image src failed status code = %s'
% (threading.current_thread().name, response.status_code))
@classmethod
def from_crawler(cls, crawler):
settings = crawler.settings
return cls(settings['IMAGES_STORE'], settings['MAXIMUM_IMAGE_NUMBER'])
существуетsettings.py
зарегистрироватьPipeline
ITEM_PIPELINES = {
'deviant_art_spider.pipelines.DeviantArtSpiderPipeline': 300,
}
Пул IP-прокси
Некоторые веб-сайты имеют механизмы защиты от сканирования, чтобы решить эту проблему, каждый запрос использует разныеIP
Прокси, есть много сайтов, которые предлагаютIP
Прокси-сервис, нам нужно написать краулер изоблачный проксипросканируй это бесплатноIP
прокси (бесплатноIP
Очень нестабильно, и после того, как я использовал прокси, различные запросы терпели неудачу Orz...).
import os
import requests
from bs4 import BeautifulSoup
class ProxiesSpider(object):
def __init__(self, max_page_number=10):
self.seed = 'http://www.ip3366.net/free/'
self.max_page_number = max_page_number # 最大页数
self.crawled_proxies = [] # 爬到的ip,每个元素都是一个dict
self.verified_proxies = [] # 校验过的ip
self.headers = {
'Accept': '*/*',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko)'
' Chrome/45.0.2454.101 Safari/537.36',
'Accept-Language': 'zh-CN,zh;q=0.8'
}
self.tocrawl_url = []
def crawl(self):
self.tocrawl_url.append(self.seed)
page_counter = 1
while self.tocrawl_url:
if page_counter > self.max_page_number:
break
url = self.tocrawl_url.pop()
body = requests.get(url=url, headers=self.headers, params={'page': page_counter}).content
soup = BeautifulSoup(body, 'lxml')
if soup is None:
print('PARSE PAGE FAILED.......')
continue
self.parse_page(soup)
print('Parse page %s done' % (url + '?page=' + str(page_counter)))
page_counter += 1
self.tocrawl_url.append(url)
self.verify_proxies()
self.download()
# 解析页面并封装
def parse_page(self, soup):
table = soup.find('table', class_='table table-bordered table-striped')
tr_list = table.tbody.find_all('tr')
for tr in tr_list:
ip = tr.contents[1].text
port = tr.contents[3].text
protocol = tr.contents[7].text.lower()
url = protocol + '://' + ip + ':' + port
self.crawled_proxies.append({url: protocol})
print('Add url %s to crawled_proxies' % url)
# 对ip进行校验
def verify_proxies(self):
print('Start verify proxies.......')
while self.crawled_proxies:
self.verify_proxy(self.crawled_proxies.pop())
print('Verify proxies done.....')
def verify_proxy(self, proxy):
proxies = {}
for key in proxy:
proxies[str(proxy[key])] = key # requests的proxies的格式必须为 协议 : 地址
try:
if requests.get('https://www.deviantart.com/', proxies=proxies, timeout=2).status_code == 200:
print('verify proxy success %s ' % proxies)
self.verified_proxies.append(proxy)
except:
print('verify proxy fail %s ' % proxies)
# 保存到文件中
def download(self):
current_path = os.getcwd()
parent_path = os.path.dirname(current_path)
with open(parent_path + '\proxies.txt', 'w') as f:
for proxy in self.verified_proxies:
for key in proxy.keys():
f.write(key + '\n')
if __name__ == '__main__':
spider = ProxiesSpider()
spider.crawl()
ПолучилIP
После пула проксиScrapy
изmiddlewares.py
Модули определяют классы промежуточного программного обеспечения прокси.
import time
from scrapy import signals
import os
import random
class ProxyMiddleware(object):
# 每次请求前从IP代理池中选择一个IP代理并进行设置
def process_request(self, request, spider):
proxy = self.get_proxy(self.make_path())
print('Acquire proxy %s ' % proxy)
request.meta['proxy'] = proxy
# 请求失败,重新设置IP代理
def process_response(self, request, response, spider):
if response.status != 200:
proxy = self.get_proxy(self.make_path())
print('Response status code is not 200,try reset request proxy %s ' % proxy)
request.meta['proxy'] = proxy
return request
return response
def make_path(self):
current = os.path.abspath('.')
parent = os.path.dirname(current)
return os.path.dirname(parent) + '\proxies.txt'
# 从IP代理文件中随机获得一个IP代理地址
def get_proxy(self, path):
if not os.path.isfile(path):
print('[LOADING PROXY] loading proxies failed proxies file is not exist')
while True:
with open(path, 'r') as f:
proxies = f.readlines()
if proxies:
break
else:
time.sleep(1)
return random.choice(proxies).strip()
Наконец вsettings.py
зарегистрироваться.
DOWNLOADER_MIDDLEWARES = {
# 这个中间件是由scrapy提供的,并且它是必需的
'scrapy.contrib.downloadermiddleware.httpproxy.HttpProxyMiddleware': 543,
# 我们自定义的代理中间件
'deviant_art_spider.middlewares.ProxyMiddleware': 540
}
End
Наш сканер изображений завершен, выполните командуscrapy crawl deviant_art_image_spider
, а затем наслаждайтесь коллекционированием картинок!
Недавно мне захотелось написать о рептилиях по прихоти, поэтому мне потребовалось время, чтобы пройтись по ней.
python
Грамматика запущена на скорую руку, а код какой-то некрасивый и недостаточно питоничный, прошу жалобу у официалов.