数据采集与融合技术第三次大作业

作业①

1.实验内容

  • 要求:指定一个网站,爬取这个网站中的所有的所有图片,例如中国气象网(http://www.weather.com.cn)。分别使用单线程和多线程的方式爬取。(限定爬取图片数量为学号后3位)
  • 输出信息:将下载的Url信息在控制台输出,并将下载的图片存储在images子文件夹中,并给出截图。
  • 运行结果截图:(运行结果较多只截取部分信息)
    (1)单线程:
    控制台截图:

文件夹截图:

(2)多线程:
控制台截图:

文件夹截图:

2.心得体会

(1)打开网页随机选择一张图片进行元素审查

可以发现我们要下载的图片网址在的src属性中,可以选用CSS语言进行爬取,然后利用requests.get()函数访问src属性中的图片链接:

html = r.text
soup=BeautifulSoup(html,"lxml")
pics=soup.select("img")
……
#省略循环语句
picture=requests.get(pic["src"])

(2)在限制总图片数时,一开始出现限制总图片数爬取但是依旧爬取多张的情况(以限制单页爬取3张,总共爬取10张为例):

向老师请教并一起讨论后发现每次爬取页面的for循环里面还有爬取单页图片的for循环,所以外面的限制对里面的执行没有起到约束作用,需要将限制写入每页爬取中:

for pic in pics:
    if(num<413):
        count+=1
        num+=1
        print("正在下载第{0}张图片".format(num))
        print(pic["src"])
        picture=requests.get(pic["src"])
        fp = open('singlepic\\' + str(pic["src"]).split('/')[-1], 'wb')  # 输入存放图片路径
        fp.write(picture.content)  # 将图片内容写进目录文件中
        fp.close()
        if(count==60):
            break

(3)对于单线程爬取,只需要按照爬虫和下载图片逻辑利用requests和Beautiful库进行编写,而多线程爬取则需要用到threading库进行线程的设置,time和random库设置休眠时间。由于本题既要爬取网页又要下载图片,所以设置了多个页面同时爬取、多个文件同时下载的多线程。
a.将num,threads等变量设置为全局变量方便整个代码中使用
b.将下载图片设置为前台线程使其先执行,用join()函数进行阻塞保证前台线程优先进行并设置一定的休眠时间:

T = threading.Thread(target=download, args=(pic,num))
T.setDaemon(False)  # 设置为前台线程
T.start()
T.join()
threads.append(T)
time.sleep(random.randint(1, 2))

c.将网页爬取设置为后台线程,用join()函数进行阻塞保证后台线程执行完主线程才继续执行,并在爬取每页间隙设置一定的休眠时间:

T=threading.Thread(target=imageSpider(start_url))
T.setDaemon(True) #设置为后台线程
T.start()
T.join()
for t in threads:
    t.join()

作业②

1.实验内容

  • 要求:使用scrapy框架复现作业①。
  • 输出信息:同作业①
  • 运行结果部分截图:
    控制台截图:

文件夹截图:

2.心得体会

(1)scrapy框架思路

(2)items:定义数据结构供Spider、pipelines调用,在items.py文件中设置img对图片地址进行存放

(3)pipelines调用items中的信息,将封装好的信息解析出来存储到本地,所以在pipelines.py文件中进行图片下载

import urllib.request
class WpicPipeline:
    count = 0
    def process_item(self, item, spider):
        url = item["img"]
        WpicPipeline.count += 1
        try:
            req = urllib.request.Request(url)
            data = urllib.request.urlopen(req, timeout=100)
            data = data.read()
            fobj = open("images/" + str(url).split("/")[-1], "wb")
            fobj.write(data)
            fobj.close()
            print(url)
            print("正在下载第{}张图片".format(WpicPipeline.count  ))
        except Exception as err:
            print(err)
            return item

(3)Spider用于发送请求并处理请求,本次实验中item需要发送两轮请求,第一轮请求爬取网页信息,通过xpath解析出图片的地址,第二轮请求根据第一轮解析出的地址发送第二轮请求,下载图片数据,并将数据封装在items中,传送给pipelines。
在spider文件夹下新建myspider.py文件并在parse函数中编写访问请求

class StocksSpider(scrapy.Spider):
    name = 'myspider' #设置名称在运行时使用
    num=0
    start_urls = ['http://www.weather.com.cn/weather/101230101.shtml',
                  'http://www.weather.com.cn/weather/101230401.shtml',
                  'http://www.weather.com.cn/weather/101230501.shtml',
                  'http://www.weather.com.cn/weather/101230201.shtml',
                  'http://www.weather.com.cn/weather/101230601.shtml',
                  'http://www.weather.com.cn/weather/101230701.shtml',
                  'http://www.weather.com.cn/weather/101230801.shtml',
                  'http://www.weather.com.cn/weather/101230301.shtml'] #要访问的页面
    def parse(self, response):
        try:
            data=response.body.decode()
            selector=scrapy.Selector(text=data)
            imgs=selector.xpath("//img/@src").extract()  #获取所以img元素的src属性值
            count=0
            for img in imgs:
                if(StocksSpider.num<413):
                    StocksSpider.num+=1
                    count+=1
                    item=WpicItem()
                    item['img']=img
                    yield item   #返回item
                else:
                    count=0
        except Exception as err:
            print(err)

(4)在setting.py文件中设置打开pipeline以及url请求头

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 = {
    'wpic.pipelines.WpicPipeline': 300,
}

(5)编写run.py文件进行运行

from scrapy import cmdline
print("061900413-李臻,请李同学下载413张图片")
cmdline.execute(['scrapy','crawl','myspider','--nolog'])

(6)存在问题:在爬虫时会出现一张图请求超时:

作业③

1.实验内容

  • 要求:爬取豆瓣电影数据使用scrapy和xpath,并将内容存储到数据库,同时将图片存储在imgs路径下。
  • 候选网站:https://movie.douban.com/top250
  • 输出信息:
序号 电影名称 导演 演员 简介 电影评分 电影封面
1 肖申克的救赎 弗兰克·德拉邦特 蒂姆·罗宾斯 希望让人自由 9.7 ./imgs/xsk.jpg
2....
  • 运行结果部分截图:
    控制台截图:

数据库截图:

2.心得体会

(1)scrapy框架思路

(2)访问网页并进行元素审查

(3)items:定义数据结构供Spider、pipelines调用,在items.py文件中设置各类参数

class DoubanmovieItem(scrapy.Item):
    # define the fields for your item here like:
    # name = scrapy.Field()
    rank = scrapy.Field() #序号
    title = scrapy.Field() #电影名称
    director = scrapy.Field() #导演
    actor = scrapy.Field() #演员
    quote = scrapy.Field() #简介
    score = scrapy.Field() #评分
    img_url=scrapy.Field() #图片地址
    pass

(4)pipelines调用items中的信息,将封装好的信息解析出来存储到本地,所以在pipelines.py文件中进行图片下载和数据库存储

class DoubanmoviePipeline:
    def __init__(self):
        self.connect = pymysql.connect(
            host=settings.MYSQL_HOST,
            db=settings.MYSQL_DBNAME,
            user=settings.MYSQL_USER,
            passwd=settings.MYSQL_PASSWD,
            charset='utf8',
            use_unicode=True) #连接数据库
        self.cursor = self.connect.cursor();
    def process_item(self, item, spider):
        url = item["img_url"]
        try:
            req = urllib.request.Request(url)
            data = urllib.request.urlopen(req, timeout=100)
            data = data.read()
            fobj = open("images/" + str(url).split("/")[-1], "wb")
            fobj.write(data)
            fobj.close()
        except Exception as err:
            print(err)
        try:
            # 插入数据
            self.cursor.execute(
                """insert into doubanmovie(no, title, director, actor, quote, score , img_url)
                value (%s, %s, %s, %s, %s, %s, %s)""",
                (item['rank'],
                 item['title'],
                 item['director'],
                 item['actor'],
                 item['quote'],
                 item['score'],
                 item['img_url']))
            # 提交sql语句
            self.connect.commit()
        except Exception as error:
            # 出现错误时打印错误日志
            print(error)
        return item

(5)Spider用于发送请求并处理请求,本次实验中item需要发送两轮请求,第一轮请求爬取网页信息,通过xpath解析出图片和电影信息的地址,第二轮请求根据第一轮解析出的地址发送第二轮请求,并将数据封装在items中,传送给pipelines。
在spider文件夹下新建myspider.py文件并在parse函数中编写访问请求,其中导演和演员的信息都在标签文本中,需要用split()函数进行分割提取。还有注意翻页的相关信息爬取。

class MovieSpider(scrapy.Spider):
    name = 'movie' #设置名称
    allowed_domains = ['movie.douban.com/top250']
    start_urls = ['http://movie.douban.com/top250']
    count=0
    def parse(self, response):
        ##遍历每一个电影相关数据
        item = DoubanmovieItem()
        movies = response.xpath("//div[ @class ='info']")
        links = response.xpath("//div[ @class ='pic']//img/@src").extract()
        for (each, link) in zip(movies, links):
            MovieSpider.count+=1
            item['rank'] = MovieSpider.count #序号
            item['title'] = each.xpath('.//span[@class ="title"][1]/text()').extract()[0] #电影名称
            if len(each.xpath('.//div[@ class ="bd"][1]/p/text()').extract()[0].split(':'))>2: #如果字符串长度大于2则包含导演和演员信息
                item['director'] = each.xpath('.//div[@ class ="bd"][1]/p/text()').extract()[0].split(':')[1].split('主演')[0].strip()
                item['actor'] = each.xpath('.//div[@ class ="bd"][1]/p/text()').extract()[0].split(':')[2].split('/')[0].strip()
            else: #否则只有演员信息
                item['director'] = each.xpath('.//div[@ class ="bd"][1]/p/text()').extract()[0].split(':')[1].split('/')[0].strip()
                item['actor'] = [' ']
            item['score'] = each.xpath('.//div[@class ="star"]/span[@ class ="rating_num"]/text()').extract()[0] #评分
            quote = each.xpath('.//p[@ class ="quote"] / span / text()').extract()
             # quote可能为空,因此需要先进行判断
            if quote:
                quote = quote[0]
            else:
                quote = ''
            item['quote'] = quote #简介
            item['img_url'] = link #图片地址
            yield item
        # 进行翻页
        next_url = response.css('span.next a::attr("href")').extract_first()
        if next_url is not None:
            url = self.start_urls[0] + next_url
            yield scrapy.Request(url=url, callback=self.parse, dont_filter=True)

(6)在setting.py文件中设置网络代理和与数据库连接相关的变量

USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:70.0) Gecko/20100101 Firefox/70.0'

# Obey robots.txt rules
ROBOTSTXT_OBEY = False
MYSQL_HOST = 'localhost'
MYSQL_DBNAME = 'douban'
MYSQL_USER = 'root'
MYSQL_PASSWD = '123456'
ITEM_PIPELINES = {
    'doubanmovie.pipelines.DoubanmoviePipeline': 300,
}

(7)编写run.py文件进行运行

from  scrapy.cmdline import execute
execute(["scrapy",'crawl','movie'])

(8)由于在piplines.py文件中设置插入数据,所以在运行前要在MySQL中新建相关数据库和表


posted @ 2021-10-28 14:39  imperceptibly  阅读(45)  评论(0编辑  收藏  举报