作业①:
要求:
用requests和BeautifulSoup库方法爬取豆瓣电影Top250数据。
每部电影的图片,采用多线程的方法爬取,图片名字为电影名
了解正则的使用方法
from bs4 import BeautifulSoup
from bs4 import UnicodeDammit
import urllib.request
import threading
import re
def imageSpider(start_url):#图片爬取
global count # 全局变量,设置这个完全是因为想名称给解决哈哈!
global threads
try:
urls = [] #最终所要得到爬取图片的网址一开始会置为空
req = urllib.request.Request(start_url, headers=headers) #引入全局变量headers改换头部信息实现多次爬取
data = urllib.request.urlopen(req) #目标网址打开
data = data.read() #对网站内容进行读操作
dammit = UnicodeDammit(data, ["utf-8", "gbk"])#用utf-8和gbk来猜测文本编码,从而设置函数最终信息编码标准
data = dammit.unicode_markup #将内容可转化为text
soup = BeautifulSoup(data, "lxml") #用lxml来解析
images = soup.select("img")#选取我们的目标(图片)的tag
for image in images:#images是存储所有的图片,而image是存取单个图片(可替换)
src = image["src"]#src存储的是图片的名称
url = urllib.request.urljoin(start_url, src)#urljoin构建绝对路径
if url not in urls:#将每一次获取得到的新的单个网址存入urls里面 避免重复
count = count+1
T = threading.Thread(target=download,args=(url,count))# 单个线程的执行操作,执行目标函数download(url,count)4
T.setDaemon(False) #确保所有的线程都已经执行完毕(后台线程不会随主线程结束而结束)
T.start() #开始执行
threads.append(T) #将该线程归为线程池,确保后续的所有线程都已经完成
#print(url)#打印已经下载的图片
except Exception as er:
print(er)
def download(url,count):#具体的下载过程
try:
count = count + 1#我是选择一开始就加一
if (url[len(url) - 4] == "."):#判断是否为目标文件(图片)
ext = url[len(url) - 4:]#获取图片的格式jdp还是png
else:
ext = ""
req = urllib.request.Request(url, headers=headers)#可以更有效的多次爬虫
data = urllib.request.urlopen(req, timeout=100)#防止超时,和第一个函数比有相同的部分,这个就不太好重复工作
data = data.read()#进行图片的数据读
fobj = open("images\" + str(count) + ext, "wb")#默认是在编译器存储在同一个文档中,命名形式用count的具体指哈哈,根据读取的格式来结尾,允许读写图片
fobj.write(data)#写数据
fobj.close()#最后要关闭这个进程
except Exception as e:
print(e)
def doubanSpider(start_url):#数据爬取
try:
req = urllib.request.Request(start_url, headers=headers) # 引入全局变量headers改换头部信息实现多次爬取
data = urllib.request.urlopen(req) # 目标网址打开
data = data.read() # 对网站内容进行读操作
print("排名","电影名称 ","导演 ","主演","上映时间","国家","电影类型","评分","评价人数 ","引用" ,"文件路径", chr(12288)) # 采用中文字符的空格填充chr(12288)
dammit = UnicodeDammit(data, ["utf-8", "gbk"])#用utf-8和gbk来猜测文本编码,从而设置函数最终信息编码标准
data = dammit.unicode_markup #将内容可转化为text
soup = BeautifulSoup(data, "lxml")
#用lxml来解析
lis = soup.select('ol[class="grid_view"] li')#选取我们的目标(图片)的tag
for li in lis:
rank=li.select('div[class="pic"] em[class=""]')[0].text.replace("em",'').replace("
",'')#排名
fn=li.select('div[class="info"] div a span ')[0].text.replace("span",'').replace("
",'')#电影名称
info_l=li.select('div[class="bd"] p')[0].text.replace("p",'').replace("
",'').replace(" ",'')#信息人员
info_f =li.select('div[class="bd"] br')[0].text.replace("br",'').replace("
",'').replace(" ",'')#电影信息
comment=li.select('div[class="bd"] div[class="star"] span[class="rating_num"]')[0].text.replace("span",'').replace("
",'').replace(" ",'')#评价信息
audi=li.select('div[class="star"] span')[3].text.replace("span",'').replace("
",'').replace(" ",'')#观影
refer=li.select('div[class="bd"] p[class="quote"]')[0].text.replace("p",'').replace("
",'').replace(" ",'')#引用
file=str(rank)+".jpg"
print(rank,fn,info_l,info_f,comment,audi,refer,file)
except Exception as er:
print(er)
start_url = "https://movie.douban.com/top250"#目标网站豆瓣
headers = {
"User-Agent": "Mozilla/5.0(Windows;U;Windows NT 6.0 x64;en-US;rv:1.9pre)Gecko/2008072421 Minefield/3.0.2pre"}
count = 0
threads = [] #线程池
imageSpider(start_url) # 调用函数
for t in threads:
t.join() #等待其他线程完成
doubanSpider(start_url)
print("over!")
心得体会:
多多调试,题目具有一定的灵活性;
没做之前觉得挺难的,做完后觉得还可以,是在自己力所能及的范围内;
任务分为两步:图片下载和数据爬取;
也发现自己的不足:知识有些遗忘,但也还好吧!之前动手实践过,所以还行
作业②:
要求:
熟练掌握 scrapy 中 Item、Pipeline 数据的序列化输出方法;Scrapy+Xpath+MySQL数据库存储技术路线爬取科软排名信息
爬取科软学校排名,并获取学校的详细链接,进入下载学校Logo存储、获取官网Url、院校信息等内容。
rankank.py
import scrapy
from ..items import RankItem
from bs4 import BeautifulSoup
import urllib.request
from bs4 import UnicodeDammit
"""
第六次作业
"""
# -*- coding: utf-8 -*-
class RankankSpider(scrapy.Spider):
name = 'rankank'
start_url = 'https://www.shanghairanking.cn/rankings/bcur/2020'
def start_requests(self):
url = RankankSpider.start_url
yield scrapy.Request(url=url, callback=self.parse)
def parse(self, response):
dammit = UnicodeDammit(response.body, ["utf-8", "gdk"])
data = dammit.unicode_markup
selector = scrapy.Selector(text=data)
lis =selector.xpath("//div[@class='rk-table-box']/table/tbody/tr")
count=0
for li in lis:
#下面为每一所大学的信息爬取
sno = li.xpath("./td[position()=1]/text()").extract_first() # 排名
sno=str(sno).strip()
sname = li.xpath("./td[@class='align-left']/a/text()").extract_first() # 名字
sname = str(sname).strip()
scity = li.xpath("./td[position()=3]/text()").extract_first() # 城市
scity = str(scity).strip()
surl = li.xpath("./td[@class='align-left']/a//@href").extract() # url
surl = str(surl).strip().replace("[",'').replace("'",'').replace("]",'')
sfile = str(count) + ".png" # 文件
##
se_url = response.urljoin(surl)
headers = {
"User-Agent": "Mozilla/5.0(Windows;U;Windows NT 6.0 x64;en-US;rv:1.9pre)Gecko/2008072421 Minefield/3.0.2pre"}
req = urllib.request.Request(se_url, headers=headers) # 引入全局变量headers改换头部信息实现多次爬取
data1 = urllib.request.urlopen(req) # 目标网址打开
data1 = data1.read() # 对网站内容进行读操作
#进而提取出图片信息与文字信息
dammit = UnicodeDammit(data1, ["utf-8", "gbk"]) # 用utf-8和gbk来猜测文本编码,从而设置函数最终信息编码标准
data1 = dammit.unicode_markup
soup1 = BeautifulSoup(data1, "lxml") # 用lxml来解析
# 获取信息
info = soup1.select('div[class="univ-introduce"] p')[0].text.replace("p",'')
sinfo = info # 信息
sinfo = str(sinfo).strip()
#获取图片
images = soup1.select('td[class="univ-logo"] img') # 选取我们的目标(图片)的tag,由于为所有图片所以之后要for
for image in images: # images是存储所有的图片,而image是存取单个图片(可替换)
url = image["src"] # src存储的是图片的名称
if (url[len(url) - 4] == "."): # 判断是否为目标文件(图片)
ext = url[len(url) - 4:] # 获取图片的格式jdp还是png
else:
ext = ""
req = urllib.request.Request(url, headers=headers) # 可以更有效的多次爬虫
data2 = urllib.request.urlopen(req, timeout=100) # 防止超时,和第一个函数比有相同的部分,这个就不太好重复工作
data2 = data2.read() # 进行图片的数据读
fobj = open("images\" + str(count) + ext, "wb") # 默认是在编译器存储在同一个文档中,命名形式用count的具体指哈哈,根据读取的格式来结尾,允许读写图片
count = count + 1
fobj.write(data2) # 写数据
fobj.close() # 最后要关闭这个进程
item = RankItem()
item["sno"] = sno
item["sname"] = sname
item["scity"] = scity
item["surl"] = surl
item["sinfo"] = sinfo
item["sfile"] = sfile
yield item
pass
pipeline.py
# 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
from itemadapter import ItemAdapter
import pymysql
class RankPipeline:
def open_spider(self, spider):
print("opened")
try:
self.con = pymysql.connect(host="127.0.0.1", port=3306, user="root", passwd="20201006Wu", db="rankank",
charset="utf8") # 链接数据库,db要是自己建的数据库
self.cursor = self.con.cursor(pymysql.cursors.DictCursor)
self.cursor.execute("delete from rankank") # 删除表格的原来内容
self.opened = True # 执行打开数据库
except Exception as error:
print(error)
self.opened = False # 不执行打开数据库
def process_item(self, item, spider):
try:
print(item["sno"],item["sname"], item["scity"], item["surl"], item["sinfo"], item["sfile"])#控制端输出信息
if self.opened:
self.cursor.execute(
"insert into rankank(sno,sname,scity,surl,sinfo,sfile)values(%s,%s,%s,%s,%s,%s)",
(item["sno"],item["sname"], item["scity"], item["surl"], item["sinfo"], item["sfile"]))
#插入sql语句
except Exception as e:
print(e)
return item
def close_spider(self, spider):
if self.opened:
self.con.commit()
self.con.close()
self.opened = False
print("closed")
items.py
# Define here the models for your scraped items
#
# See documentation in:
# https://docs.scrapy.org/en/latest/topics/items.html
import scrapy
class RankItem(scrapy.Item):
# define the fields for your item here like:
# name = scrapy.Field()
sno=scrapy.Field()#排名
sname = scrapy.Field()#名字
scity = scrapy.Field()#城市
surl = scrapy.Field()#url
sinfo = scrapy.Field()#信息
sfile = scrapy.Field()#文件
pass
run.py
from scrapy import cmdline
cmdline.execute("scrapy crawl rankank -s LOG_ENABLED=False".split())
settings.py
BOT_NAME = 'rank'
SPIDER_MODULES = ['rank.spiders']
NEWSPIDER_MODULE = 'rank.spiders'
ROBOTSTXT_OBEY = False
ITEM_PIPELINES = {
'rank.pipelines.RankPipeline': 300,
}
心得体会:
不是特别难和上题一样,具有一定的灵活性;
任务分为三个:图片下载,数据爬取,连接数据库;
是上一题的进阶版>o<
作业③:
要求:
熟练掌握 Selenium 查找HTML元素、爬取Ajax网页数据、等待HTML元素加载、网页跳转等内容。
使用Selenium框架+ MySQL数据库存储技术模拟登录慕课网,并获取学生自己账户中已学课程的信息并保存在MYSQL中。
其中模拟登录账号环节需要录制gif图。
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
import urllib.request
import threading
import sqlite3
import os
import datetime
from selenium.webdriver.common.keys import Keys
import time
class icourse:
header = {
"User-Agent": "Mozilla/5.0(Windows;U;Windows NT 6.0 x64;en-US;rv:1.9pre)Gecko/2008072531 Minefield/3.0.2pre"
}
#这个恒定不变的!
def start(self, url):#开始只需要利用url模拟浏览器搜索爬取
chrome_options = Options()#调用chrome的浏览器
chrome_options.add_argument("——headless")
chrome_options.add_argument("——disable-gpu")
self.driver = webdriver.Chrome(chrome_options=chrome_options)
self.id=0#编号
try:
self.con = sqlite3.connect("icourse.db")
#链接到指定数据库,事实证明是是运行的文件的下面的直接建立,额外再补充一下:这里的数据库也是表格
self.cursor = self.con.cursor()
try:
self.cursor.execute("drop table icourse")
#初始操作删除stock的数据库的原来所有内容
except:
pass#报错直接忽略
try:
sql = "create table icourse(id varchar(16) ,course varchar(64),college varchar(64),teacher varchar(64),team varchar(256),count varchar(256),process varchar(128),brief varchar(512))"
#所以我们只需要建一个数据库具体的表的为这句语句实现,指定为stock的表
self.cursor.execute(sql)
#表示执行上面的语句
except:
pass#报错直接忽略
except Exception as err:
print("start")#报错显示其原因
theurl='https://www.icourse163.org/'
self.driver.get(theurl) # 浏览器得到地
##嘻嘻我还是有办法找到简单的方法,就是先模拟登录通过theurl,
##完成登录后在跳转到指定的网站把信息爬取下来
def closeUp(self):#关闭数据库与浏览器
#这个部分和第一题是一样的
try:
self.con.commit()
self.con.close()#数据库关闭
self.driver.close()#浏览器关闭
except Exception as err:
print("close")#报错显示其原因
def insertDB(self, id,course,college,teacher,team,count,process,brief):
#将从浏览器爬取得到的数据插入数据库
try:
sql = "insert into icourse (id,course,college,teacher,team,count,process,brief) values (?,?,?,?,?,?,?,?)"
#因为每次爬取的都是不同的数据,所以这个sql语句是含有参数的表示
self.cursor.execute(sql, (id,course,college,teacher,team,count,process,brief))
#执行sql指令
except Exception as err:
print("insert")#报错显示其原因
def showDB(self):
#实现在pycharm的控制台输出从浏览器导出到mysql结果
try:
con = sqlite3.connect("icourse.db")
#第一步肯定是建立链接
cursor = con.cursor()
print("id","course","college","teacher","team","count","process","brief")#控制台显示的一排数据
cursor.execute("select id,course,college,teacher,team,count,process,brief from icourse order by id")#sql语句获取数据
#第二步执行mysql语句,得到的结果用rows暂存,所以之后我们还需要将rows分隔开打印
rows = cursor.fetchall()
for row in rows:
print(row[0], row[1], row[2], row[3], row[4],row[5], row[6], row[7])
#分隔开打印数据
con.close()#读取完数据,关闭数据库
except Exception as err:
print("show")
def execute(self, url):#在控制台输出正在进行的过程
self.start(url)
self.loginmooc()
print("Starting")
self.driver.get(url) # 浏览器得到真正的网址地
print("Processing")
self.process()
print("Closing")
self.closeUp()
print("Completed")
def loginmooc(self):
self.driver.maximize_window()#页面呈现为最大的形式
self.driver.find_element_by_xpath("//a[@class='f-f0 navLoginBtn']").click()#选择登录|注册
time.sleep(2)
self.driver.find_element_by_xpath("//span[@class='ux-login-set-scan-code_ft_back']").click()#选择“其他方式”
time.sleep(1.2)
self.driver.find_elements_by_xpath("//ul[@class='ux-tabs-underline_hd']//li")[1].click()#在列表中选择“手机”
time.sleep(0.3)
iframe_id = self.driver.find_elements_by_tag_name("iframe")[1].get_attribute('id')
self.driver.switch_to.frame(iframe_id)
#寻找下面的输入位置
self.driver.find_element_by_xpath("//input[@id='phoneipt']").send_keys('18196534898')#
time.sleep(3)
self.driver.find_element_by_xpath("//input[@class='j-inputtext dlemail']").send_keys('$$$$$$$$$$$$')
time.sleep(1.1)
self.driver.find_element_by_xpath("//a[@class='u-loginbtn btncolor tabfocus ']").click()
time.sleep(4.7)
self.driver.get(self.driver.current_url)
def process(self):#真正的爬取数据开始!
time.sleep(1)
try:
lis = self.driver.find_elements_by_xpath("//div[@class='m-course-list']/div/div[@class]")
#除图片以外其他所有信息均在这个目录下
for li in lis:
self.id = self.id + 1
id=self.id
course= li.find_element_by_xpath(".//div[@class='t1 f-f0 f-cb first-row']").text#课程
college =li.find_element_by_xpath(".//a[@class='t21 f-fc9']").text#学院名
teacher=li.find_element_by_xpath(".//div[@class='t2 f-fc3 f-nowrp f-f0']/a[position()=2]").text#老师
team=li.find_element_by_xpath(".//div[@class='t2 f-fc3 f-nowrp f-f0']").text#所有的团队成员
count = li.find_element_by_xpath(".//span[@class='hot']").text#总人数数
brief = li.find_element_by_xpath(".//span[@class='p5 brief f-ib f-f0 f-cb']").text#课程的简介
process=li.find_element_by_xpath(".//span[@class='txt']").text#课程的时间进展
self.insertDB(id,course,college,teacher,team,count,process,brief )
time.sleep(1)
except Exception as err:
print("process")
url = "https://www.icourse163.org/search.htm?search=%E8%89%BA%E6%9C%AF#/"#还是和上题同一个网址
spider = icourse()
icourse().execute(url)
icourse().showDB()#没有()会报错因为self
心得体会:
不是特别难;
任务分为三个:登录页面,爬取数据;
对我而言,是上一次作业的进阶版
但不得不提就是这行代码的重要性,不然会报错:
selenium.common.exceptions.NoSuchElementException: Message: no such element: Unable to locate element: {"method":"xpath","selector":"//input[@id='phoneipt']"}
(Session info: chrome=86.0.4240.198)
代码:
iframe_id = self.driver.find_elements_by_tag_name("iframe")[1].get_attribute('id')
self.driver.switch_to.frame(iframe_id)
#寻找下面的输入位置
写一下这门课给我的感受:
爬虫主要还是要动手实践,理论很多;
实际操作会发现:没有预料的难;
谢谢老师告诉爬取各种网页的方式,
虽然在做实验的时候,觉得有些很繁琐,每次都要学习新的知识是`(>﹏<)′
学的过程要保持一颗平淡的心,
一步一步慢慢做,出现问题也不要过于急躁,总是有解决的方法!