Scrapy 爬虫入门

284 阅读5分钟

1. scrapy 项目目录
scrapy是通过命令运行的爬虫框架

scrapy startproject projectname // 创建scrapy项目
通过命令创建scrapy项目后,会自动生成一个projectname的文件夹,其中包括一个spiders文件夹,以及items.py、pipelines.py、middlewares.py、settings.py等python文件。spiders文件夹用于放置不同的spider文件,items.py中用于定义需要获取的数据容器,pipelines.py中定义的数据进一步加工与处理(如存放数据库、下载图片等),middlewares.py中定义自定义的中间件,一般用于自定义去重url逻辑、给request添加消息头模拟浏览器、定义ip池等,settings.py为爬虫的设置文件。

2. 爬虫items
import scrapy
class AppsSpiderItem(scrapy.Item):
app_id = scrapy.Field()
platform = scrapy.Field()
...
定义结构化数据信息的格式如下:

结构化数据名 = scrapy.Feild()
对于每一个需要提取的数据均需要定义对应的结构化数据名

3. 定义爬虫
Spider类是Scrapy中与爬虫相关的一个基类,所有爬虫文件必须继承该类(scrapy.Spider)

在爬虫项目中可以通过genspider命令自动在spiders文件夹中创建一个爬虫

scrapy genspider name domain
scrapy genspider test www.baidu.com
import scrapy

class TestSpider(scrapy.Spider):
name = 'test'
allowed_domains = ['www.baidu.com']
start_urls = ['http://www.baidu.com/']

def parse(self, response):
pass
默认生成的spider只重写了parse方法,运行时其会自动调用默认的start_requests(),从start_urls中获取url发起请求,请求完成后会默认调用parse方法,在parse中编写具体解析逻辑。

4. 通过post请求url
默认生成的request是通过get请求,当需要同过post请求时可以通过重写start_requests方法,添加formdata

class TestSpider(scrapy.Spider):
name = 'test'
allowed_domains = ['www.baidu.com']
start_urls = ['http://www.baidu.com/']

def start_requests(self):
url = self.start_urls[0]
formdata = {'name':'mike'}
yield scrapy.FormRequest(url=url, formdata=formdata, callback=self.parse)

def parse(self, response):
pass
自定义的request需要显式地定义callback方法

5. 运行爬虫
默认的运行方式是通过命令调用爬虫

scrapy runspider test.py (爬虫文件名)
scrapy crawl test(爬虫名)
当想通过idea运行scrapy爬虫时,可以通过py文件模拟cmdline

创建run.py文件,在其中定义

from scrapy import cmdline

name = 'google_play'
cmd = 'scrapy crawl {0}'.format(name) + ' --nolog'
cmdline.execute(cmd.split())
这种方式运行的爬虫可以在idea中debug爬虫,推荐使用

6. BeautifulSoup解析网页
解析网页一般通过正则表达式,xpath和BeautifulSoup解析,在此简要介绍BeautifulSoup的常用方法

BeautifulSoup是将复杂的HTML文档转化为一个复杂的树型结构,每个节点都是一个python对象,所有对象可以归纳为4种:

Tag
NavigableString
BeautifulSoup
Comment
1). Tag

Tag表示HTML中的一个个标签,获得一个节点后,可以通过 .tagname 获得其子tag,如

soup.div
soup.a
这种方式当且仅当其这种子节点类型只有一个时使用,当其子节点同一类型有多个时,可硬通过 .contents获得其所有子节点的集合。

soup.contents
.attrs 获取标签的所有属性

print(soup.p.attrs)
#{'class': ['title'], 'name': 'dromouse'}
获得某个属性

print(soup.p['class']
#['title']
print(soup.p.get('class'))
#['title']
2). NavigableString

当想要获取标签的内容时可以通过 .string获得

print soup.p.string
#The Dormouse's story
3). BeautifuSoup

BeautifulSoup对象表示一个文档的全部内容

4). Comment (一般不用)

常用方法:

description = soup.find_all('div', attrs={'class': 'W4P4ne '}) // 获得特定属性的div标签的集合
description[0].meta.attrs['content'] // 获得集合中第一个标签的meta子标签的‘content’属性值
7. 模拟浏览器 自定义user-agent
一般情况下,我们需要不停更换用户代理,降低被ban的风险。默认情况下,scrapy会给所有的request一个默认的user-agent。当需要自定义user-agent时,具体实现是通过request对象的headers属性,以及自定义一个downloadmiddleware。

class AppSpiderDownloaderMiddleWare(object):

def __init__(self, user_agent_list):
self.user_agent = user_agent_list

@classmethod
def from_crawler(cls, crawler, *args, **kwargs):
middleware = cls(crawler.settings.get('MY_USER_AGENT'))
return middleware

def process_request(self, request, spider):
#随机选择一个user-agent
request.headers['user-agent'] = random.choice(self.user_agent)
在上列中,我们自定义了一个下载中间件,这样就可以保证每一个请求在交给下载器之前都会经过这里,同时在创建中间件时,传入了定义在settings中的user-agent集合。

MY_USER_AGENT = ["Mozilla/5.0+(Windows+NT+6.2;+WOW64)+AppleWebKit/537.36+(KHTML,+like+Gecko)+Chrome/45.0.2454.101+Safari/537.36",
"Mozilla/5.0+(Windows+NT+5.1)+AppleWebKit/537.36+(KHTML,+like+Gecko)+Chrome/28.0.1500.95+Safari/537.36+SE+2.X+MetaSr+1.0",
"Mozilla/5.0+(Windows+NT+6.1;+WOW64)+AppleWebKit/537.36+(KHTML,+like+Gecko)+Chrome/50.0.2657.3+Safari/537.36",
"Mozilla/5.0+(Windows+NT+6.1;+WOW64)+AppleWebKit/537.36+(KHTML,+like+Gecko)+Chrome/51.0.2704.106+Safari/537.36",
"Mozilla/5.0+(Windows+NT+6.1)+AppleWebKit/537.36+(KHTML,+like+Gecko)+Chrome/47.0.2526.108+Safari/537.36+2345Explorer/7.1.0.12633",
"Mozilla/5.0+(Macintosh;+Intel+Mac+OS+X+10_11_4)+AppleWebKit/537.36+(KHTML,+like+Gecko)+Chrome/49.0.2623.110+Safari/537.36",
"Mozilla/5.0+(Macintosh;+Intel+Mac+OS+X+10_9_5)+AppleWebKit/537.36+(KHTML,+like+Gecko)+Chrome/42.0.2311.152+Safari/537.36",
"Mozilla/5.0+(Windows+NT+6.1;+WOW64)+AppleWebKit/537.36+(KHTML,+like+Gecko)+Chrome/42.0.2311.152+Safari/537.36",
"Mozilla/5.0+(Macintosh;+Intel+Mac+OS+X+10_10_2)+AppleWebKit/600.3.18+(KHTML,+like+Gecko)+Version/8.0.3+Safari/600.3.18",
"Mozilla/5.0+(Windows+NT+6.1;+WOW64)+AppleWebKit/537.36+(KHTML,+like+Gecko)+Chrome/49.0.2623.22+Safari/537.36+SE+2.X+MetaSr+1.0",
"Mozilla/5.0+(Macintosh;+Intel+Mac+OS+X+10_11_4)+AppleWebKit/601.5.17+(KHTML,+like+Gecko)+Version/9.1+Safari/601.5.17",
"Mozilla/5.0+(Windows+NT+6.1;+WOW64)+AppleWebKit/537.36+(KHTML,+like+Gecko)+Chrome/48.0.2564.103+Safari/537.36",
"Mozilla/5.0+(Windows+NT+6.1;+WOW64)+AppleWebKit/537.36+(KHTML,+like+Gecko)+Chrome/47.0.2526.80+Safari/537.36+Core/1.47.640.400+QQBrowser/9.4.8309.400",
"Mozilla/5.0+(Macintosh;+Intel+Mac+OS+X+10_10_5)+AppleWebKit/600.8.9+(KHTML,+like+Gecko)+Version/8.0.8+Safari/600.8.9",
"Mozilla/5.0+(Windows+NT+6.3;+WOW64)+AppleWebKit/537.36+(KHTML,+like+Gecko)+Chrome/39.0.2171.99+Safari/537.36+2345Explorer/6.4.0.10356",
"Mozilla/5.0+(Windows+NT+6.1;+WOW64)+AppleWebKit/537.36+(KHTML,+like+Gecko)+Chrome/45.0.2454.87+Safari/537.36+QQBrowser/9.2.5584.400",
"Mozilla/5.0+(Windows+NT+6.1;+WOW64)+AppleWebKit/537.36+(KHTML,+like+Gecko)+Chrome/47.0.2526.111+Safari/537.36",
"Mozilla/5.0+(Windows+NT+6.1;+WOW64)+AppleWebKit/537.36+(KHTML,+like+Gecko)+Chrome/33.0.1750.146+BIDUBrowser/6.x+Safari/537.36",
"Mozilla/5.0+(Windows+NT+6.1)+AppleWebKit/537.36+(KHTML,+like+Gecko)+Chrome/39.0.2171.99+Safari/537.36+2345Explorer/6.5.0.11018",
"Mozilla/5.0+(Windows+NT+6.2;+WOW64)+AppleWebKit/537.36+(KHTML,+like+Gecko)+Chrome/42.0.2311.154+Safari/537.36+LBBROWSER"]
然后在settings.py文件中开启Middleware配置

# Enable or disable downloader middlewares
# See doc.scrapy.org/en/latest .…
DOWNLOADER_MIDDLEWARES = {
'apps_spider.middlewares.AppsSpiderDownloaderMiddleware': 543,
}
8. 设置ip池
在settings文件中定义ip集合

IPPOOL=[
{"ipaddr":"61.129.70.131:8080"},
{"ipaddr":"61.152.81.193:9100"},
{"ipaddr":"120.204.85.29:3128"},
{"ipaddr":"219.228.126.86:8123"},
{"ipaddr":"61.152.81.193:9100"},
{"ipaddr":"218.82.33.225:53853"},
{"ipaddr":"223.167.190.17:42789"}
]
自定义中间件,在每次request前随机添加ip地址

import random
from scrapy import signals
from myproxies.settings import IPPOOL

class ProxiesSpiderMiddleware(object):

def __init__(self,ip=''):
self.ip=ip

def process_request(self, request, spider):
thisip=random.choice(IPPOOL)
print("this is ip:"+thisip["ipaddr"])
request.meta["proxy"]="http://"+thisip["ipaddr"]
在settings文件中启用这个中间件

# Enable or disable downloader middlewares
# See doc.scrapy.org/en/latest .…
DOWNLOADER_MIDDLEWARES = {
'apps_spider.middlewares.ProxiesSpiderMiddleware': 543,
}
ip地址可以在免费ip,或在github中搜索proxy ip获得

9. 保存数据到json
在pipelines中编写处理items的逻辑

import codecs
import json
import urllib.request

class AppsSpiderPipeline(object):
icon_path = "./icons"

def __init__(self):
self.file = codecs.open("./apps.json", "wb", encoding="utf-8")

def process_item(self, item, spider):
d = dict(item)
icon_url = d.pop('icon_url')

# 下载图片
local_path = self.icon_path + '/' + d['app_id'] + '.jpg'
urllib.request.urlretrieve(icon_url, filename=local_path)

# 持久化到json
i = json.dumps(d, ensure_ascii=False)
line = i + '\n'
self.file.write(line)

return item

def close_spider(self, spider):
self.file.close()
在settings中启用pipelines

# Configure item pipelines
# See doc.scrapy.org/en/latest/t…
ITEM_PIPELINES = {
'apps_spider.pipelines.AppsSpiderPipeline': 300,
}
10.解决ajax异步加载内容
某些网页第一次请求仅获取部分内容,当用户浏览到页面底端才通过js再次加载新的内容。对于这种页面,只需在每次请求得到的response中定位到下次请求的网址,即可递归爬取到该页面所有内容。以google play列表页为例:






















---------------------
作者:koibiki
来源:CSDN
原文:blog.csdn.net/koibiki/art…
版权声明:本文为博主原创文章,转载请附上博文链接!

更多免费技术资料可关注:annalin1203