前言

既上一篇文章之后发现自己对scrapy的爬取流程还是不太理解,于是自己做了个小脚本用来练习。因为爬取到信息较少,但是涉及的功能没有减少,因此也更直观更好理解了。

昨天运行成功了就没管,今天想记录一下的时候,发现….要爬取的网站炸了。

  1. 锁定爬取网站

    name: 可以理解为爬虫的名字

    allowed_domains:过滤机制,可以在爬取过程中过滤掉不是这个域名的网页,但不会对start_urls生效

    start_urls:顾名思义,第一个爬取的网页

    parse:链接处理,如果有多个主页面的话可以用循环批量处理,但这次的目标只有一个页面,因此直接调用parse_list处理了
    关于yield:简单说就是一个不终止当前方法的 return ,将参数传给后面的函数(如下面的scrapy.Request)处理,而自己接着完成未完成的任务。

    关于信息传递:这个问题困扰了我很久,一直想知道scrapy中不同的函数(方法)是怎么联系起来共同处理一件事情的?其实根本还在于yield(当然我现在也没理解透彻)。仔细观察就能发现yield scrapy.Request(url=url,callback=self.parse_list)其是包含的信息很多,首先通过Request获取网页并解析,再通过callback调用响应函数并将解析的网页传递过去,从而完成各函数间的调用。

    1
    2
    3
    4
    5
    6
    7
    8
    class MovieSpider(scrapy.Spider):
    name = 'movie'
    allowed_domains = ['www.oeecc.com']
    start_urls = ['http://www.oeecc.com/music/']

    def parse(self, response):#获取页码链接
    url = "http://www.oeecc.com/list/top.html"
    yield scrapy.Request(url=url,callback=self.parse_list)
  2. 爬取主页面信息

    这里使用的爬取数据方法时Xpath,在网页检查,找到要爬取的信息,复制xpath即可(如下图)

    通过观察网页可以知道,每个歌曲的信息都被存储在一个li标签中

我们经过xpath筛选可以得到n个li标签以及里面的内容,因此通过循环取出单个li标签,再次筛选出我们需要的信息即可。

因为我要爬取的是歌曲链接,因此我提取的是二级网页的链接

1
2
3
4
5
6
7
def parse_list(self, response):#爬取总页面
li_list = response.xpath('/html/body/div[2]/div[3]/ul/li')
for li in li_list:
url =li.xpath('a/@href').extract_first()
url = 'http://www.oeecc.com' + url
urllist.append(url)
yield scrapy.Request(url=url,callback = self.parser_info)
  1. 爬取二级网页

    通过上个函数的yield scrapy.Request(url=url,callback = self.parser_info),可以知道它将二级网页的url解析并传递到了parser_info中,下面就是parser_info的内容

    1
    2
    3
    4
    5
    6
    7
    def parser_info(self, response):#爬取info信息
    info = MusicItem()
    info['title'] = response.xpath('/html/body/div[2]/div[2]/div[1]/div[2]/h1/a/text()').extract_first()
    url_initially = response.xpath('/html/body/div[2]/div[2]/div[3]/p[2]').extract_first()
    url_final = re.findall(r'value="(.*?)"', url_initially, re.S)
    info['url'] = url_final
    yield info

    具体步骤和主页面相似,re.findall(r’value=”(.*?)”‘, url_initially, re.S)是正则表达语句。

源码

movie
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import re
import scrapy

from tttest.items import MusicItem, urlItem


class Douban250Spider(scrapy.Spider):
name = 'douban250'
allowed_domains = ['www.oeecc.com']
start_urls = ['http://www.oeecc.com/list/top.html']

def parse(self, response):#获取页码链接
url = "http://www.oeecc.com/list/top.html"
yield scrapy.Request(url=url,callback=self.parse_list)


def parse_list(self, response):#爬取总页面
li_list = response.xpath('/html/body/div[2]/div[3]/ul/li')
urllist=[]
for li in li_list:
url =li.xpath('a/@href').extract_first()
url = 'http://www.oeecc.com' + url
urllist.append(url)
yield scrapy.Request(url=url,callback = self.parser_info)


def parser_info(self, response):#爬取info信息
info = MusicItem()
info['title'] = response.xpath('/html/body/div[2]/div[2]/div[1]/div[2]/h1/a/text()').extract_first()
url_initially = response.xpath('/html/body/div[2]/div[2]/div[3]/p[2]').extract_first()
url_final = re.findall(r'value="(.*?)"', url_initially, re.S)
info['url'] = url_final
yield info
pipelines
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# Define your item pipelines here
#
# Don't forget to add your pipeline to the ITEM_PIPELINES setting
# See: https://docs.scrapy.org/en/latest/topics/item-pipeline.html


# useful for handling different item types with a single interface
import pymysql
from itemadapter import ItemAdapter

from tttest.items import MusicItem


class TttestPipeline:
def __init__(self):
self.connect = pymysql.connect(
host='',
port=3391,
db='',
user='',
passwd='',
charset='utf8',
use_unicode=True,
cursorclass=pymysql.cursors.DictCursor
)
self.cursor = self.connect.cursor()

def process_item(self, item, spider):

if isinstance(item,MusicItem):
self.MusicItem(item)

return item
#
#
def MusicItem(self,item):
try:
self.cursor.execute('''
SELECT * FROM movie WHERE title=%s
''',(item["title"],))
film = self.cursor.fetchone()
if film is None:
self.cursor.execute('''
INSERT INTO movie(title,url)
VALUE(%s,%s)
''',(item['title'],item['url']))
else:
self.cursor.execute('''
UPDATE movie
SET url = %s
WHERE title = %s
''',(item['url'],item['title']))
self.connect.commit()
except Exception as err:
print("错误"+str(err))

成果(?)

部分歌曲音频不完整,这是歌曲源的问题。

本来是想把爬下来的音乐全放到这里的,没想到音乐链接每天都会改,隔天就过期。。。总之这个网站不适合练习爬虫,但本帖本意是总结scrapy的用法,也算达到目的了。