IpSpider类
上篇已经创建了第一个scrapy工程,新建了第一个爬取类IpSpider,用来从网站(或一组网站)中提取信息。它们必须子类化 scrapy.Spider并定义要生成的初始请求,可选择如何跟踪页面中的链接,以及如何解析下载的页面内容以提取数据。代码如下图所示:
IpSpider类定义的一些属性和方法:
- name:识别spider。它在项目中必须是唯一的,也就是说,您不能为不同的Spiders设置相同的名称。
- start_requests():必须返回Spider将开始爬行的可迭代请求(您可以返回请求列表或编写生成器函数)。后续请求将从这些初始请求中连续生成。
- parse():将调用一个方法来处理为每个请求下载的响应。响应参数是TextResponse保存页面内容的实例,并具有处理它的其他有用方法。
该parse()方法通常解析响应,将抽取的数据提取为dicts,并查找要遵循的新URL并Request从中创建新的request()。
运行周期
首先生成初始请求以爬网第一个URL,并指定要使用从这些请求下载的响应调用的回调函数。
第一个执行请求是通过调用 start_requests()(默认情况下)为在请求中作为回调函数的方法中Request指定的URL start_urls和parse方法生成的 方法获得的。
在回调函数中,您解析响应(网页)并返回带有提取的数据,Item对象, Request对象或这些对象的可迭代的dicts。这些请求还将包含一个回调(可能相同),然后由Scrapy下载,然后由指定的回调处理它们的响应。
在回调函数中,您通常使用选择器解析页面内容 (但您也可以使用BeautifulSoup,lxml或您喜欢的任何机制)并使用解析的数据生成项目。
最后,从spider返回的项目通常会持久保存到数据库(在某些项目管道中)或使用Feed导出写入文件。
start_requests 返回一个可迭代的请求,也可以用start_url替代,start_requests函数代码举例如下:
import scrapy
class IpSpider(scrapy.Spider):
name = "myIp"
allowed_domains = ["xicidaili.com"]
# start_urls = [
# "https://www.xicidaili.com/nn/1",
# "https://www.xicidaili.com/nn/2"
# ]
def start_requests(self):
urls = ["https://www.xicidaili.com/nn/1",
"https://www.xicidaili.com/nn/2"]
for url in urls:
yield scrapy.Request(url=url, callback=self.parse)
def parse(self, response):
filename = response.url.split("/")[-1]
print(filename)
with open(filename, 'wb') as f:
f.write(response.body)
执行结果是一样的。
提取数据
学习如何使用Scrapy提取数据的最佳方法是使用shell Scrapy shell尝试选择器。跑:
scrapy shell 'https://www.xicidaili.com/nn/1'
当从命令行运行Scrapy shell时,请记住始终将URL括在引号中,否则包含参数(即&字符)的url 将不起作用。在Windows上,请使用双引号:scrapy shell “https://www.xicidaili.com/nn/1”
使用shell,您可以尝试使用CSS和响应对象选择元素(还可以使用xpath形式):
response.css('title')
结果为:
“[<Selector xpath=‘descendant-or-self::title’ data=’<title>国内高匿免费HTTP代理IP__第1页国内高匿’>]”
response.css('title::text').extract()
结果为:
[‘国内高匿免费HTTP代理IP__第1页国内高匿’]
取第一个元素:
response.css(‘title::text’).extract_first()
结果为:
‘国内高匿免费HTTP代理IP__第1页国内高匿’
使用.extract_first()避免IndexError和返回 None。
获取需要的ip、端口、协议等信息:
import scrapy
class IpSpider(scrapy.Spider):
name = "myIp"
allowed_domains = ["xicidaili.com"]
start_urls = [
"https://www.xicidaili.com/nn/1",
"https://www.xicidaili.com/nn/2"
]
def parse(self, response):
trs = response.css('#ip_list tr')
for tr in trs:
td = tr.css('td')
if len(td) == 0:
continue
yield {
"ip":td.css('td::text')[0].extract(),
"protocal":td.css('td::text')[5].extract(),
"port":td.css('td::text')[1].extract(),
"isGN":td.css('td::text')[4].extract()
}
存储数据
scrapy crawl myIp -o myIp.json
json中存在中文乱码,可以在setting.py文件中修改默认的输出编码方式,只需要在setting.py中增加如下语句(默认似乎是没有指定的,所以要增加,如果默认有,就直接修改)
FEED_EXPORT_ENCODING = ‘utf-8’
新增下一页爬取
import scrapy
class IpSpider(scrapy.Spider):
name = "myIp"
allowed_domains = ["xicidaili.com"]
start_urls = [
"https://www.xicidaili.com/nn/1"
]
def parse(self, response):
trs = response.css('#ip_list tr')
for tr in trs:
td = tr.css('td')
if len(td) == 0:
continue
yield {
"ip":td.css('td::text')[0].extract(),
"protocal":td.css('td::text')[5].extract(),
"port":td.css('td::text')[1].extract(),
"isGN":td.css('td::text')[4].extract()
}
next_page = response.css('.pagination a.next_page::attr("href")').extract_first()
if next_page is not None and int(next_page.split('/')[-1]) < 10: #(暂限制爬取9页)
next_page = response.urljoin(next_page)
yield scrapy.Request(next_page, callback=self.parse)