数据采集第三次作业

作业一

要求

指定一个网站,爬取这个网站中的所有的所有图片,例如中国气象网(http://www.weather.com.cn)。分别使用单线程和多线程的方式爬取。(限定爬取图片数量为学号后3位)

输出信息

将下载的Url信息在控制台输出,并将下载的图片存储在images子文件夹中,并给出截图。

作业过程

结果展示

单线程:控制台输出十分整齐

多线程:控制台输出顺序被打乱,但是不用等待,加快了爬取的速度

分析网页

因为需要爬取的是整个网站的117张图片,肯定是不够这么多图片的,所以先把整个网站的url先保存下来,再逐一进行爬取。

观察得到图片的url地址似乎都在img标签下:

代码部分

编写函数得到网页的url,似乎都在a标签的href属性中:

def get_urls(start_url):#定义函数获取到网址中的所有链接
    try:
        global all_urls
        req = urllib.request.Request(start_url, headers=headers)
        data = urllib.request.urlopen(req, timeout=100)
        data = data.read()
        dammit = UnicodeDammit(data, ["utf-8", "gbk"])
        data = dammit.unicode_markup
        soup = BeautifulSoup(data, "lxml")
        urls = soup.select("a")#天气网的所有链接都在a标签下
        for url in urls:
            try:
                href = url["href"]
                if href[0] == 'h':
                     all_urls.append(href)
            except Exception as err:
                print(err)
    except Exception as err:
        print(err)

编写函数下载图片保存到本地:

def download(url,count):#定义下载图片函数
    try:
        if(url[len(url)-4]=="."):#提取文件后缀扩展名 .jpg .png?
            ext=url[len(url)-4:]
        else:
            ext=""
        req=urllib.request.Request(url,headers=headers)
        data=urllib.request.urlopen(req,timeout=100)
        data=data.read()
        root = 'E:/数据融合实践/第三次实验/images/'
        fobj=open(root+str(count)+ext,"wb")
        fobj.write(data)
        fobj.close()
        print("第" + str(count) + "张图片已保存" + " 下载地址:" + url)  #控制台输出图url
    except Exception as err:
	    print(err)

编写函数爬取网页内图片的url:似乎使用css语法直接定位img标签得到属性比较快速。

def imageSpider(web_url):
    global count
    global flag
    try:
        global image_urls
        req=urllib.request.Request(web_url,headers=headers)
        data=urllib.request.urlopen(req,timeout=100)
        data=data.read()
        dammit=UnicodeDammit(data,["utf-8","gbk"])
        data=dammit.unicode_markup
        soup=BeautifulSoup(data,"lxml")#获取html文档
        images=soup.select("img")#直接获取所有img标签
        for image in images:
            try:
                src=image["src"]
                image_url=urllib.request.urljoin(web_url,src)#获得所要爬取的图片地址
                if image_url not in image_urls:
                    image_urls.append(image_url)
                    download(image_url)
                    count = count + 1
                    if count == 117:
                        flag = 1
                        return False
            except Exception as err:
                print(err)
    except Exception as err:
        print(err)

多线程在单线程基础上添加如下代码即可:

      T = threading.Thread(target=download, args=(image_url,count))
      T.setDaemon(False)
      T.start()
      threads.append(T)

threads = []#定义列表存储线程
for i in range(len(all_urls)):
    print("目前爬取的网页url是:" + all_urls[i])
    if flag == 1:
        break
    imageSpider(all_urls[i])
    for t in threads:
        t.join()

作业心得

1.本次作业是对书本上代码的复现,在此基础上做了稍微的改动,体会到了多线程和单线程爬取网站的差别。
2.对于多线程代码的编写还不够熟悉,编写速度较慢,需要更多的练习。
3.再次巩固了css语法对标签的定位与request请求部分,对此部分内容更加熟悉了。

作业二

要求

使用scrapy框架复现作业①。

输出信息

同作业①

作业过程

结果展示

代码部分

weather.py:
因为要爬取这个网页的所有代码,所以要先获得网页的url,再到每个url中爬取图片。具体实现如下代码所示:

class WeatherSpider(scrapy.Spider):
    name = 'weatherspider'
    start_url = 'http://www.weather.com.cn//'

    def start_requests(self):
        yield scrapy.Request(self.start_url,callback=self.parse_page)
        #这里先调用parse_page函数获取所有的网页中全部的url

    def parse_image(self, response):#获取图片地址
        try:
            data = response.body.decode("utf-8")
            selector = scrapy.Selector(text=data)
            item = WeatherItem()
            item["image_urls"] = selector.xpath('//img//@src').extract()#得到图片的url
            yield item
        except Exception as err:
            print(err)

    def parse_page(self, response):#获取页面中网页的地址
        try:
            yield scrapy.Request(self.start_url, callback=self.parse_image)
            data = response.body.decode("utf-8")
            selector = scrapy.Selector(text=data)
            allpage_urls = selector.xpath('//a//@href').extract()
            for page_url in allpage_urls:
                if page_url[0:4]=='http':#提取出网页的url,再爬取此网页的图片
                    yield scrapy.Request(page_url, callback=self.parse_image)
                    #调用parse_image函数获取当前网页中所有图片地址
        except Exception as err:
            print(err)

settings.py:配置文件

ROBOTSTXT_OBEY = False
DEFAULT_REQUEST_HEADERS = {#配置请求头
    "User-Agent": "Mozilla/5.0 (Windows; U; Windows NT 6.0 x64; en-US; rv:1.9pre) Gecko/2008072421 Minefield/3.0.2pre"
}
ITEM_PIPELINES = {#修改pipelines
   'weather.pipelines.WeatherPipeline': 300,
}

item.py:设计项目类,此部分只有一个url链接

class WeatherItem(scrapy.Item):
    # define the fields for your item here like:
    # name = scrapy.Field()
    image_urls = scrapy.Field() #图片链接

pipelines.py:管道类保存数据

import urllib.request

count = 1
headers = {
"User-Agent": "Mozilla/5.0 (Windows; U; Windows NT 6.0 x64; en-US; rv:1.9pre) Gecko/2008072421 Minefield/3.0.2pre"}
class WeatherPipeline:
    def process_item(self, item, spider):
        global count
        for image_url in item["image_urls"]:
            if count <= 117:
                if (image_url[len(image_url) - 4] == "."):  # 提取文件后缀扩展名 .jpg .png?
                    ext = image_url[len(image_url) - 4:]
                else:
                    ext = ""
                req = urllib.request.Request(image_url, headers=headers)
                data = urllib.request.urlopen(req, timeout=100)
                data = data.read()
                root = 'E:/ScrapyExample/weather/images/'
                fobj = open(root + str(count) + ext, "wb")
                fobj.write(data)
                fobj.close()
                print("第" + str(count)+"张图片已保存"+"下载地址:"+image_url)
            count += 1
        # return item

作业心得

1.对scraoy框架比较的不熟悉,导致花了很多时间。
2.在请教了老师和同学之后,得到解决问题的思路,加深了对scrapy框架的理解。

作业三

要求

爬取豆瓣电影数据使用scrapy和xpath,并将内容存储到数据库,同时将图片存储在imgs路径下

候选网站

https://movie.douban.com/top250

输出信息

序号 电影名称 导演 演员 简介 电影评分 电影封面
1 肖申克的救赎 弗兰克·德拉邦特 蒂姆·罗宾斯 希望让人自由 9.7 ./imgs/xsk.jpg
2....

作业过程

结果展示

控制台输出,下载的图片,在数据库中查看如下图所示

分析网页

这道题目需要使用xpath框架进行爬取豆瓣网页的数据,F12分析网页标签树,可以清楚的找到所需爬取的内容。

这里发现导演和演员信息不全,所以我选择先打开此标签下的url链接,到此链接中再进行爬取。

使用右键点击需要爬取的元素,可以快速复制xpath路径,得到元素路径,方便代码的编写:

代码部分

scrapy框架下有四部分代码:
Movie.py:编写weaherspider类,对网页进行爬取

class WeatherSpider(scrapy.Spider):
    name = "moviespider"
    start_url = 'https://movie.douban.com/top250'
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.121 Safari/537.36'
    }

    def start_requests(self):#发送请求
        yield scrapy.Request(self.start_url,callback=self.parse)

    def parse(self, response):
        try:
            data = response.body.decode("utf-8")#得到网页html文档
            selector = scrapy.Selector(text=data)#创建select对象

            lis = selector.xpath("//*[@id='content']/div/div[1]/ol/li")#一部电影对应一个li标签
            for li in lis:
                rank = li.xpath("./div/div[1]/em/text()").extract_first()
                name = li.xpath("./div/div[2]/div[1]/a/span[1]/text()").extract_first()
                introduce = li.xpath("./div/div[2]/div[2]/p[2]/span/text()").extract_first()
                score = li.xpath("./div/div[2]/div[2]/div/span[2]/text()").extract_first()
                image_url = li.xpath("./div/div[1]/a/img/@src").extract_first()#这几项可以直接xpath得到
                #导演和演员属性在此网页下表示不全,在另一个网页中进行提取
                director_url = li.xpath("./div/div[2]/div[1]/a/@href").extract()#得到包含演员全部信息的url
                # print(director_url[0])
                resp = requests.get(director_url[0], headers=self.headers)
                resp.encoding = "utf-8"
                page = resp.text
                selector = scrapy.Selector(text=page)
                director = selector.xpath('//*[@id="info"]/span[1]/span[2]//text()').extract()
                director = ''.join(director)
                actor = selector.xpath('//*[@id="info"]/span[3]/span[2]//text()').extract()#得到全部演员信息列表
                actor = ''.join(actor)#将列表转换为str方便后续存入sql数据库中

                item = MovieItem()
                item["rank"] = rank
                item["name"] = name
                item["director"] = director
                item["actor"] = actor
                item["introduce"] = introduce
                item["score"] = score
                item["image_url"] = image_url
                yield item
        except Exception as err:
            print(err)

settings.py:配置文件

ROBOTSTXT_OBEY = False
DEFAULT_REQUEST_HEADERS = {
    "User-Agent": "Mozilla/5.0 (Windows; U; Windows NT 6.0 x64; en-US; rv:1.9pre) Gecko/2008072421 Minefield/3.0.2pre"
}#加个头
ITEM_PIPELINES = {
   'movie.pipelines.MoviePipeline': 300,
}

item.py:设计项目类

class MovieItem(scrapy.Item):
    # define the fields for your item here like:
    # name = scrapy.Field()
    rank = scrapy.Field()#序号
    name = scrapy.Field()#电影名称
    director = scrapy.Field()#导演
    actor = scrapy.Field()#演员
    introduce = scrapy.Field()#简介
    score = scrapy.Field()#电影评分
    image_url = scrapy.Field()#电影封面
    pass

pipelines.py:管道类保存数据
在此部分进行控制台输出,封面的保存以及保存数据到数据库中

class MoviePipeline:
    count = 0
    def process_item(self, item, spider):
        try:
            connect = pymysql.connect(host="localhost", user="root", password="*******", database="scrapy",charset='utf8')  #连接数据库
            cur = connect.cursor()  #建立游标
            creatable = ''' 
                 create table if not exists movie(
                        序号 int(5) not null,
                        名称 char(20) not null,
                        导演 char(40) not null,
                        演员 text(200) not null,
                        简介 char(50) not null,
                        评分 char(10) not null,
                        封面 char(80) not null)
               '''
            cur.execute(creatable)
            sql = '''INSERT INTO movie(序号,名称,导演,演员,简介,评分,封面) VALUES("%s","%s","%s","%s","%s","%s","%s")'''
            arg = (int(item['rank']), item['name'], item['director'], item['actor'], item['introduce'], item['score'],item['image_url'])
            cur.execute(sql, arg)
            connect.commit()
        except Exception as err:
            print(err)

        print(item["rank"] + "\t" +item['name'] + "\t" +  item['director'] + "\t" + item['actor'] + "\t" + item['introduce'] + "\t" +
              item['score'] + "\t" + item['image_url'])#控制台输出
        urllib.request.urlretrieve(item["image_url"], "./image/" + item["rank"] + ".jpg")#下载图片

        return item

心得体会

1.加深了对scrapy框架的理解,加快了编写代码的速度。
2.学习了如何在pycharm中编写mysql代码,并将数据存到mysql数据库中。
3.对于数据库的相关知识,需要更多的学习。
代码链接:https://gitee.com/linyu17/crawl_project/tree/master/第三次作业

posted @ 2021-10-28 22:07  今晚一定12点前睡觉  阅读(15)  评论(0编辑  收藏  举报