(1)
问题:爬取7天的 天气情况 日期 天气状况温度 风力–> 保存到CSV
分析需求 要干什么事情 通过什么技术来解决
爬取7天的 天气情况 日期 天气状况温度 风力–> 保存到CSV
具体步骤(分析页面)
1.先明确目标url
通过分析我们发现要爬取的数据都在 ul class=“t clearfix” 这个标签当中,然后我们就去网页的源代码中确定了
2.先获取网页的源代码 整个html文件
3.从网页的源代码当中去匹配ul标签的数据
4.从ul标签里面去匹配li标签的数据
5.去解析li标签里面的数据
5.保存数据
import requests
import re
import csv
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.128 Safari/537.36'
}
class WeatherSpider:
# 获取网页源代码
# 类对象 实例对象 实例方法 类方法
def getSource(self):
# 目标url
url = 'http://www.weather.com.cn/weather/101250101.shtml'
resp = requests.get(url,headers=headers)
# print(resp.content.decode('utf-8'))
return resp.content.decode('utf-8')
# 解析数据 保存数据
def parseSource(self):
content = self.getSource()
# 匹配ul 正则表达式比较灵活 .*?ul标签前面的数据
# 匹配并获取的 (<ul class="t clearfix">.*?</ul>) .*?ul标签后面的数据
result = re.match(r'.*?(<ul class="t clearfix">.*?</ul>).*?',content,re.S)
# print(result.group(1))
# 匹配li
ul = result.group(1)
lis = re.findall(r'<li.*?">.*?</li>',ul,re.S)
lst_all = [] # 保存所有的天气数据
pattern = re.compile(r'<li.*?">.*?<h1>(.*?)</h1>.*?<p.*?>(.*?)</p>.*?<i>(.*?)</i>.*?<i>(.*?)</i>.*?</li>',re.S)
for li in lis:
# lst_one = [] # 保存一天的天气数据
r = pattern.match(li)
# print(r.group(1),end='')
# print(r.group(2),end='')
# print(r.group(3),end='')
# print(r.group(4),end='')
# print()
lst_one = [r.group(1),r.group(2),r.group(3),r.group(4)]
lst_all.append(lst_one)
return lst_all
# 保存数据
def saveData(self):
content = self.parseSource()
with open('weather7day.csv','w',encoding='utf-8',newline='') as file_obj:
writer = csv.writer(file_obj)
writer.writerow(['日期','天气','温度','风力'])
writer.writerows(content)
def main():
WeatherSpider().saveData()
if __name__ == '__main__':
main()
(2)
豆瓣top250电影
问题: 爬取 电影的名字 评分 引言 详情页的url 保存到csv文件当中
具体步骤(分析页面)
1 明确url (是否是静态网页,对比源代码与检查)
2 先像目标url发起请求 获取网页源码
3 可以把网页源码通过 etree.HTML 生成一个element对象
element对象 通过xpath进行导航 电影的名字 评分 引言 详情页的url
4 我们可以把数据先保存到一个字典里面 {title:‘肖申克的救赎’,‘start’:9.7…} ,{title:‘霸王别姬’,‘start’:9.7…} 在把这些字典保存到一个列表当中
5 把列表中的数据存到csv文件当中
import requests
from lxml import etree
import csv
# 目标Url
doubanUrl = 'https://movie.douban.com/top250?start={}&filter='
# 定义一个函数 获取网页源码
def getSource(url):
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.128 Safari/537.36'
}
response = requests.get(url,headers=headers)
response.encoding = 'utf-8'
return response.text
# 解析数据 电影的名字 评分 引言 详情页的url
def getEveryItem(source):
html_element = etree.HTML(source)
# class="info" 电影的名字 评分 引言 详情页的url 25
movieItemList = html_element.xpath('//div[@class="info"]')
movieList = []
for eachMoive in movieItemList:
movieDict = {}
title = eachMoive.xpath('div[@class="hd"]/a/span[@class="title"]/text()') # 标题
otherTitle = eachMoive.xpath('div[@class="hd"]/a/span[@class="other"]/text()') # 副标题
link = eachMoive.xpath('div[@class="hd"]/a/@href')[0] # url
star = eachMoive.xpath('div[@class="bd"]/div[@class="star"]/span[@class="rating_num"]/text()')[0] # 评分
quote = eachMoive.xpath('div[@class="bd"]/p[@class="quote"]/span/text()') # 引言(名句)
# 第一种异常处理
# 第二种非空判断
if quote:
quote = quote[0]
else:
quote = ''
movieDict['title'] = ''.join(title + otherTitle) # 主标题要 + 父标题
movieDict['url'] = link
movieDict['star'] = star
movieDict['quote'] = quote
print(movieDict)
movieList.append(movieDict)
return movieList
# 保存数据 次数 Ip检测 代理Ip(付费)
def writeData(movieList):
with open('douban.csv','w',encoding='utf-8',newline='') as file_obj:
writer = csv.DictWriter(file_obj,fieldnames=['title','star','quote','url'])
writer.writeheader()
for each in movieList:
writer.writerow(each)
if __name__ == '__main__':
movieList = []
for i in range(10):
pageLink = doubanUrl.format(i * 25)
source = getSource(pageLink)
movieList = getEveryItem(source) # 保存的最后一页 movieList = movieList + getEveryItem(source) a = 1 a += 1
writeData(movieList)
(3)
问题:爬取中国天气网 所有城市对应的温度把数据保存到csv当中
具体步骤(分析页面)
目标url
http://www.weather.com.cn/textFC/hb.shtml 华北
http://www.weather.com.cn/textFC/db.shtml 东北
1> 我们发现每一个url 对应一个大区。所以我们先搞定一个 在去搞定其他的
2> 1. 先找到整页的 div=conMidtab 标签的数据
2. 接下来去找每一个省或者直辖市所对应的table标签
3. 找table标签里面的tr标签(需要注意 要把前2个tr过滤掉)
4. 去tr标签里面找td标签(第0个是城市 倒数第二个是温度)
import requests
from bs4 import BeautifulSoup
import csv
# 保存到csv文件当中 列表里面 [{城市:xxx,温度:7},{}....]
titles = ('city','temp')
def pares_page(url):
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.128 Safari/537.36'
}
response = requests.get(url,headers=headers)
# print(response.content.decode('utf-8'))
text = response.content.decode('utf-8')
# 网页解析
# 1.先找到整页的 div=conMidtab 标签的数据
# html5lib来解析网页 pip isntall html5lib,补全网页源代码
soup = BeautifulSoup(text,'html5lib')
conMidtab = soup.find('div',class_='conMidtab')
# 2. 接下来去找每一个省会或者直辖市所对应的table标签
tables = conMidtab.find_all('table')
# 定义一个列表 保存最终的数据 [{},{}]
lst = []
for table in tables:
# 3. 找table标签里面的tr标签(需要注意 要把前2个tr过滤掉)
trs = table.find_all('tr')[2:]
for index,tr in enumerate(trs): # enumerate() 返回的是 下标所以以及对应的值
tds = tr.find_all('td')
city_td = tds[0] # 直辖市/省
if index == 0:
city_td = tds[1]
info = {}
'''
从打印的结果发现了如果我们取0的时候 只有省的第一个城市(省会)是不对的。其它的数据都是正确的。改成1的时候只有省会是对的,其它的就都是错误的。
我们什么情况下 取第一个数据 tds[1] 省会
'''
city = list(city_td.stripped_strings)[0] # 城市,*取字符串的套路*
temp_td = tds[-2]
temp = list(temp_td.stripped_strings)[0] # 温度
info['city'] = city
info['temp'] = temp
lst.append(info)
print('city:',city,'temp:',temp)
# break # 先打印北京的
return lst
def writeData(lst):
with open('wather.csv','w',encoding='utf-8',newline='') as file_obj:
writer = csv.DictWriter(file_obj,titles)
writer.writeheader()
writer.writerows(lst)
def main():
lst = []
# 目标url 先去搞定一个区域 在去搞定其它的区域
url = 'http://www.weather.com.cn/textFC/hb.shtml' # 华北
url = 'http://www.weather.com.cn/textFC/db.shtml' # 东北
url = 'http://www.weather.com.cn/textFC/gat.shtml' # 港澳台
urls = ['http://www.weather.com.cn/textFC/hb.shtml','http://www.weather.com.cn/textFC/db.shtml','http://www.weather.com.cn/textFC/gat.shtml']
for url in urls:
lst += pares_page(url)
writeData(lst)
if __name__ == '__main__':
main()
(4)
问题:爬取 海贼王吧 爬取桌面壁纸
具体步骤(分析页面)
1.我们发现要爬取的数据是图片,那么就只要找到它(每张图片)的src 就可以了
经过分析发现网页源码当中没有我们想要爬取的数据。然后我们确定它是经过ajax加载的数据
一般情况下 ajax请求都在 XHR这个选项(XMLHttpRequest)
通过network 继续找真实的数据接口
即找到真正的目标url
2.解析数据
可以把(response)数据通过json (利用json.cn)转换成Python的数据类型的字典(preview)。 然后通过key-value形式找到 每张图片的url
也可以通过正则(对(response)数据直接进行解析)
代码:
import requests
import re
import time
'''
https://tieba.baidu.com/photo/g/bw/picture/list?kw=%E6%B5%B7%E8%B4%BC%E7%8E%8B&alt=jview&rn=200&tid=1934517161&pn=1&ps=1&pe=40&info=1&_=1620822548466
https://tieba.baidu.com/photo/g/bw/picture/list?kw=%E6%B5%B7%E8%B4%BC%E7%8E%8B&alt=jview&rn=200&tid=1934517161&pn=1&ps=40&pe=79&wall_type=h&_=1620823519114
https://tieba.baidu.com/photo/g/bw/picture/list?kw=%E6%B5%B7%E8%B4%BC%E7%8E%8B&alt=jview&rn=200&tid=1934517161&pn=1&ps=79&pe=118&wall_type=h&_=1620823534332
ps=1 ps=40 ps=79 pe=40 pe=79 pe=118 规律 39
'''
name = 1
for i in range(1,80,39):
# 目标url
# url = 'https://tieba.baidu.com/photo/g/bw/picture/list?kw=%E6%B5%B7%E8%B4%BC%E7%8E%8B&alt=jview&rn=200&tid=1934517161&pn=1&ps=1&pe=40&info=1&_=1620822548466'
url = 'https://tieba.baidu.com/photo/g/bw/picture/list?kw=%E6%B5%B7%E8%B4%BC%E7%8E%8B&alt=jview&rn=200&tid=1934517161&pn=1&' + '&ps=' + str(i) + '&pe=' + str(39 + i) + '&wall_type=h&_=1620823534332'
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.128 Safari/537.36'
}
res = requests.get(url,headers=headers)
# print(res.text)
# 正则表达式匹配数据
img_urls = re.findall('"murl":"(.*?)"',res.text)
for img_url in img_urls:
img_response = requests.get(img_url)
# 保存数据
print('正在下载第%d张图片' % name)
with open('img/%d.jpg'%name,'wb') as file_obj:
# time.sleep(0.5)
file_obj.write(img_response.content)
name += 1
time.sleep(1)
(5)
问题:通过selenium登录豆瓣
具体步骤
人怎么做,selenium就怎么做 时刻记住:先定位,再操作
# 登录豆瓣
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import Select
import time
driver = webdriver.Chrome()
driver.get('https://www.douban.com/')
# 切换iframe 灵活变通
'''
NoSuchElementException: Message: no such element: Unable to locate element: {"method":"css selector","selector":".nojs"}
iframe作祟
'''
login_frame = driver.find_element_by_xpath('//*[@id="anony-reg-new"]/div/div[1]/iframe')
driver.switch_to.frame(login_frame)
# 切换登录方式 千万不要忘记click()
# 当属性有空格的时候 account-tab-account on 如何解决呢?
# 1.可以选其中的一部分(通过测试),技巧:选较长的一段 2 xpath来定位
driver.find_element_by_class_name('account-tab-account').click()
time.sleep(2)
# 定位账号和密码 并输入内容
driver.find_element_by_id('username').send_keys('xxxxxx')
time.sleep(1)
driver.find_element_by_id('password').send_keys('xxxxxx')
time.sleep(1)
# 点击登录按钮
driver.find_element_by_class_name('btn').click()
(6)
问题:通过selenium获取cookie(条件:自己不久之前已登过)
具体步骤(分析页面)
获取cookies–>解析数据–>测试
from selenium import webdriver
import requests
import time
import json
# 模拟登录自己的QQ空间
# 携带cookie进行模拟 也就是我们先通过正常的途径进行登录此时此刻就可以获得一个cookie.然后在正常的编写一个 模拟登录的逻辑就可以啦
# 下面的逻辑 1 要通过selenium获取 qq空间的cookie值 2 解析这个cookie值 3 进行测试
#要通过selenium获取 qq空间的cookie值
#把逻辑封装一下 类 方法
driver = webdriver.Chrome()
# 不要删参数 加载第三方的登录方式
driver.get('https://xui.ptlogin2.qq.com/cgi-bin/xlogin?proxy_url=https%3A//qzs.qq.com/qzone/v6/portal/proxy.html&daid=5&&hide_title_bar=1&low_login=0&qlogin_auto_login=1&no_verifyimg=1&link_target=blank&appid=549000912&style=22&target=self&s_url=https%3A%2F%2Fqzs.qzone.qq.com%2Fqzone%2Fv5%2Floginsucc.html%3Fpara%3Dizone&pt_qr_app=%E6%89%8B%E6%9C%BAQQ%E7%A9%BA%E9%97%B4&pt_qr_link=http%3A//z.qzone.com/download.html&self_regurl=https%3A//qzs.qq.com/qzone/v6/reg/index.html&pt_qr_help_link=http%3A//z.qzone.com/download.html&pt_no_auth=0')
time.sleep(1)
button = driver.find_element_by_class_name('face')
button.click()
time.sleep(2)
listCookies = driver.get_cookies() # 是python中的list json.loads()
# # 以下的逻辑可以不写 不实现 目的是保存listCookies,方便后续调用
# jsonCookies = json.dumps(listCookies) # 把python的数据类型转换成json类型的字符串(str)
# print(type(jsonCookies),jsonCookies)
#
# with open('qqzone.json','w') as file_obj:
# file_obj.write(jsonCookies)
# 解析cookie数据 列表推导式 [] 返回的结果是一个新的列表
cookie = [item['name'] + '=' + item['value'] for item in listCookies]
cookie_str = '; '.join(cookie)
#item for item in cookie
print(cookie_str)
# 测试代码 selenium获取和解析之后的cookie是否可以使用
url = 'https://user.qzone.qq.com/2023203294'
headers = {
'cookie':cookie_str,
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36'
}
html = requests.get(url,headers=headers)
time.sleep(2)
with open('qzong.html','w',encoding='utf-8') as file_obj:
file_obj.write(html.text)
print(html.text)
方法总结:
- requests 获取URL响应
- selenium 登陆及操作网页(including:输入框、按钮、下拉框、获取cookie)
- 三种方法解析html(正则、xpath、bs4)
实施过的案例种类:爬取天气,保存csv;豆瓣top250电影信息,保存csv;爬取图片,并保存
案例总结:(写代码之前,须对网页检查有充分理解)
1》获取html–>并解析->后保存
- 明确目标URL(注意对ajax处理)
- 得html
- 解析(三种)(先整体,后局部)
- 保存,注意格式
2》登陆网页,获取cookie
用selenium(requests麻烦且难)
(7)
问题:使用selenium获取猫眼电影top100电影信息 [电影排名 电影名称 主演 上映时间 评分]
分析页面结构 选择合适的技术点
1>明确目标的url https://maoyan.com/board/4
2>我们先搞定一页的电影数据 再去搞定其它页的
3>通过分析 数据都是在dl标签里面 dl标签里面每一个dd标签它就是一部电影
细节:
1》翻页的处理
- 当我们点击到最后一页的时候 发现下一页的按钮没有了 就可以判断是最后一页了
2》报错的处理
from selenium import webdriver
import csv
# # 创建chrome设置对象 程序没有问题在去设置 无界面模式
# options = webdriver.ChromeOptions()
# # 设置无界面功能 --headless 浏览器无界面 --xxxx
# options.add_argument('--headless')
# #selenium获取猫眼电影top100电影信息
driver = webdriver.Chrome()
driver.get('https://maoyan.com/board/4')
def get_one_page():
# 找到这一页的dd标签 千万不要忘记写dd
dd_lst = driver.find_elements_by_xpath('//*[@id="app"]/div/div/div[1]/dl/dd')
single_list = []
for dd in dd_lst:
# text属性 获取当前dd节点的子子孙孙的文本内容
# 视情况而分析 验证我们打印的内容有什么规律吗?如果有 就进一步操作
# print(dd.text) #str类型 要测试其结果是啥
# print('*'*80)
one_film_info_lst = dd.text.split('\n')
item = {}
try:
item['rank'] = one_film_info_lst[0].strip()
item['name'] = one_film_info_lst[1].strip()
item['actor'] = one_film_info_lst[2].strip()
item['time'] = one_film_info_lst[3].strip()
item['score'] = one_film_info_lst[4].strip()
except:
pass
single_list.append(item)
print(item)
return single_list
all_list = []
while True:
single_list = get_one_page()
# if 不是最后一页:
# driver.find_element_by_link_text('下一页').click()
# else:
# driver.quit()
# break
all_list += single_list
try:
# 找不到最后一页 就会抛出异常 此时就证明是最后一页了
driver.find_element_by_link_text('下一页').click()
except Exception as e:
driver.quit()
break
with open('maoyetop100.csv', 'w', encoding='utf-8', newline='') as file_obj:
dictwriter = csv.DictWriter(file_obj, fieldnames=['rank', 'name', 'actor', 'time', 'score'])
dictwriter.writeheader()
dictwriter.writerows(all_list)
(8)
需求:selenium爬取京东某商品数据
分析页面结构 选择合适的技术点
1>我们发现 所有的数据都是在一个ul标签 ul标签下面每一个li标签对应的就是一本书。
2>我们拖动 拖动条(滚轮)的时候 页面又加载了数据
代码中 当我们进入这个页面的时候,把这个拖动条拖动一下,拖到最下面,然后等它加载一会儿,等页面元素加载完了之后,我们再去抓取
如何拖动 拖动条?(我们会用到一个 加载js的方法)
细节:
1》翻页的处理
- 当我们点击到最后一页的时候 发现下一页的按钮还有 要用find()方法
2》如何拖动 拖动条
3》报错的处理
from selenium import webdriver
import time
class JdSpider():
def __init__(self):
# 设置无界面
self.options = webdriver.ChromeOptions()
# 设置无界面功能 --headless 浏览器无界面 --xxxx
self.options.add_argument('--headless')
self.driver = webdriver.Chrome(options=self.options)
self.driver.get('https://www.jd.com/')
# 定位输入框和按钮
self.driver.find_element_by_xpath('//*[@id="key"]').send_keys('爬虫书')
time.sleep(1)
self.driver.find_element_by_xpath('//*[@id="search"]/div/div[2]/button').click()
time.sleep(1)
def pares_html(self):
# 进入这个页面的时候,把这个拖动条拖动一下,拖到最下面
# 0 是从去起始位置开始 document.body.scrollHeight 整个窗口的高度 模板!
self.driver.execute_script(
'window.scrollTo(0,document.body.scrollHeight)'
)
time.sleep(2)
# 提取数据 千万不要忘记写li
li_lst = self.driver.find_elements_by_xpath('//*[@id="J_goodsList"]/ul/li')
for li in li_lst:
# print(li.text)
# print('*'*50)
try:
item = {}
item['price'] = li.find_element_by_xpath('.//div[@class="p-price"]/strong').text.strip()
item['name'] = li.find_element_by_xpath('.//div[@class="p-name"]/a/em').text.strip()
item['commit'] = li.find_element_by_xpath('.//div[@class="p-commit"]/strong').text.strip()
item['shop'] = li.find_element_by_xpath('.//div[@class="p-shopnum"]/a').text.strip()
print(item)
except Exception as e:
print(e)
def main(self):
while True:
self.pares_html()
#
if self.driver.page_source.find('pn-next disable') == -1:
self.driver.find_element_by_xpath('//*[@id="J_bottomPage"]/span[1]/a[9]').click()
time.sleep(1)
else:
self.driver.quit()
break
if __name__ == '__main__':
spider = JdSpider()
spider.main()
(9)
需求:获取12306购票订单
具体步骤(分析网页,选择合适的技术点)
1 登录 2 车次以及余票查询 3 解析车次列表 4 确认乘客信息及席别 5 核对信息
经验总结:
- 字典的操作不太了解
- 老师的代码没有真正理解,就去码了,导致中途一度停止
- 运行前,保证代码没有明显错误(没有红线等)
- 特别注意有许多缩进的地方,(for try if)
- 一开始码的时候,分析页面(具体步骤) 一定要明确
预定车票的标准:根据车次,然后是否是二等或一等(二等优先级高)
选择席别的标准:二等或一等(二等优先级高)
- selenium定位时 (属性 元素)
- 显式等待那里EC的方法 (url 属性值 元素)
#爬取12306订单
import csv
import time
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import Select
from selenium.common.exceptions import NoSuchElementException
#驱动器须定义在类的外面,因为python的垃圾回收机制
driver=webdriver.Chrome()
class spider(object):
def __init__(self,from_station,to_station,travel_date,train_rank,passengers):#train_rank={'G106':['M','O']}
self.login_url='https://kyfw.12306.cn/otn/resources/login.html'
self.personer_url='https://kyfw.12306.cn/otn/view/index.html'
self.spider_url='https://kyfw.12306.cn/otn/leftTicket/init?linktypeid=dc'
self.confirm_url='https://kyfw.12306.cn/otn/confirmPassenger/initDc'
self.to_station=to_station
self.from_station=from_station
self.travel_date=travel_date
self.travel_rank=train_rank
self.passengers=passengers
self.reader_dict={}
self.__init__csv()#一开始码的时候,这行代码没写
self.number=None
#需要在整个class中调用的属性,先在这里声明
#初始化城市代号
def __init__csv(self):
with open('stations.csv','r',encoding='utf-8') as file_obj:
readers=csv.DictReader(file_obj)
for reader in readers:
name=reader['name']
code=reader['code']
self.reader_dict[name]=code
#登录
def login(self):
driver.get(self.login_url)
WebDriverWait(driver,100).until(
EC.url_contains(self.personer_url)
)
print('登录成功!')
def spider_analyse(self):
driver.get(self.spider_url)
driver.implicitly_wait(3)
driver.find_element_by_id('gb_closeDefaultWarningWindowDialog_id').click()
#设置出发地
fromTag=driver.find_element_by_id('fromStation')
code=self.reader_dict[self.from_station]
driver.execute_script('arguments[0].value="%s"'%code,fromTag)
#设置目的地
toTag = driver.find_element_by_id('toStation')
code = self.reader_dict[self.to_station]
driver.execute_script('arguments[0].value="%s"' % code, toTag)
#设置日期
date=driver.find_element_by_id('train_date')
driver.execute_script('arguments[0].value="%s"' % self.travel_date, date)
#点击查询
time.sleep(3)
query=driver.find_element_by_xpath('//*[@id="query_ticket"]')
driver.execute_script('arguments[0].click()',query)
WebDriverWait(driver,10).until(
EC.presence_of_element_located((By.XPATH,'//tbody[@id="queryLeftTable"]/tr'))
)
trains_list=driver.find_elements_by_xpath('//tbody[@id="queryLeftTable"]/tr[not(@datatran)]')#定位属性不是datatran的tr标签
is_searched=False
for i in trains_list:
try:
train_list=i.text.replace('\n',' ').split(' ')
# print(train_list)
# print('*'*20)
num=train_list[0]
if num in self.travel_rank:#判断车次是否在self.travel_rank中
# for rank in self.travel_rank[num]:
# if rank=='O':
# pd=train_list[10]
# if pd =='有' or pd.isdigit():
# is_searched=True
# break
# if rank=='M':
# pd=train_list[9]
# if pd =='有' or pd.isdigit():
# is_searched=True
# break
#关键就是这里的解析部分,代码出错集中在这里
pd1=train_list[10]
pd2=train_list[9]
if pd1=='有' or pd1.isdigit() or pd2=='有' or pd2.isdigit():
is_searched=True
if is_searched:
time.sleep(2)
self.number=num
order_btn = i.find_element_by_xpath('//*[@id="ticket_5l0000G106B2_01_10"]/td[13]/a')
order_btn.click()
except:
pass
def confirm_information(self):
WebDriverWait(driver,100).until(
EC.url_contains(self.confirm_url)
)
#确认乘客
labels=driver.find_elements_by_xpath('//ul[@id="normal_passenger_id"]/li/label')
for label in labels:
name=label.text
if name in self.passengers:
label.click()
#确认席别
select=Select(driver.find_element_by_id('seatType_1'))
for i in self.travel_rank[self.number]:
try:
select.select_by_value(i)
except NoSuchElementException:#可能select.select_by_value(i)找不到
continue
else:
break
submit_btn = driver.find_element_by_xpath('//*[@id="submitOrder_id"]')
submit_btn.click()
# 封装了基本的功能
def run(self):
# 登录
self.login()
# 车次以及余票查询并解析
self.spider_analyse()
# 确认乘客信息
self.confirm_information()
def main():
result=spider('上海','北京','2021-05-24',{'G106':['O','M']},['name1','name2'])
result.run()
if __name__ == '__main__':
main()
(10)
需求: 爬取古诗文网站中的 诗词的 标题 作者 朝代 诗句 以及翻页 并保存
页面分析:
域名:[‘gushiwen.org’, ‘gushiwen.cn’]
每一首诗都在class="sons"里
1.爬虫程序
import scrapy
from poems.items import PoemsItem
class PoemSpider(scrapy.Spider):
name = 'poem'
allowed_domains = ['gushiwen.org','gushiwen.cn']
start_urls = ['https://www.gushiwen.org/default_1.aspx']
def parse(self, response):
div_list=response.xpath('//div[@class="sons"]/div[@class="cont"]')
for div in div_list:
name=div.xpath('.//b/text()').get()
# print(name)
source=div.xpath('.//p[@class="source"]/a/text()').getall()
try:
author=source[0]
dynasty=source[1]
content=div.xpath('.//div[@class="contson"]/text()').getall()
# print(content)
result=''.join(content).strip()
# print(result)
item = PoemsItem(name=name,author=author,dynasty=dynasty,result=result)
yield item
except:
print(name)
#翻页处理:
next_href = response.xpath('//a[@id="amore"]/@href').get()
# 翻页
if next_href:
next_url = response.urljoin(next_href) # urljoin()可以进行url地址的补全
# request = scrapy.Request(next_url)
# yield request
yield scrapy.Request(
url=next_url,
callback=self.parse # 如果这个逻辑是这个parse 就可以省略
)
2.piplines
import json
class PoemsPipeline:
def open_spider(self, spider):
self.fp = open('ancient_poems.txt', 'w', encoding='utf-8')
def process_item(self, item, spider):
# 这个Item从爬虫文件过来的时候不是一个字典对象。它是通过PoemsItem()实例化出来的对象
item_json = json.dumps(dict(item), ensure_ascii=False) # 输出中文
self.fp.write(item_json + '\n')
# print(item)
return item
def close_spider(self, spider):
self.fp.close()
(11)
需求:爬取腾讯招聘网职位及对应职责
页面分析:
1.是否为静态—>动态(ajax)
2.翻页 在scrapy中:1>列出几个URL,然后.format 2>直接找下一页的Url地址 最后 yield scrapy.Request(url,callback=None)
3.实现 (爬虫程序、items、piplines.py)
注:若需要点击跳转到另一个URL 这时看看检查里是否有现成的、找规律
检查、response、preview、网页源代码、json
import scrapy
import json
from recruit.items import RecruitItem
class PositionSpider(scrapy.Spider):
name = 'position'
allowed_domains = ['tencent.com']
first_url='https://careers.tencent.com/tencentcareer/api/post/Query?timestamp=1622635924738&countryId=&cityId=&bgIds=&productId=&categoryId=&parentCategoryId=&attrId=&keyword=&pageIndex={}&pageSize=10&language=zh-cn&area=cn'
second_url='https://careers.tencent.com/tencentcareer/api/post/ByPostId?timestamp=1622637678428&postId={}&language=zh-cn'
start_urls = [first_url.format(1)]
def parse(self, response):
# 解析数据 re xpath bs4..
# response.encoding='utf-8'
data = json.loads(response.text)
for job in data['Data']['Posts']:
item = RecruitItem()
item['job_name'] = job['RecruitPostName']
post_id = job['PostId']
# 获取详情页的url
detail_url = self.second_url.format(post_id)
#对每一个职位,再次请求url获取职位
yield scrapy.Request(
url=detail_url,
callback=self.detail_content,
meta={'item':item}
)
# print(item)
# 翻页
for page in range(2,3):
url = self.first_url.format(page)
yield scrapy.Request(url=url)
def detail_content(self,response):
# 数据有没有过来
# item = response.meta['item']
item = response.meta.get('item')
data = json.loads(response.text)
item['job_duty'] = data['Data']['Responsibility']
print(item)
(12)
古诗文代码的扩展(进一步爬取每一首古诗的译文与注释)
poem.py
import scrapy
from poems.items import PoemsItem
import re
from lxml import etree
import requests
import time
class PoemSpider(scrapy.Spider):
name = 'poem'
allowed_domains = ['gushiwen.org','gushiwen.cn']
start_urls = ['https://www.gushiwen.org/default_1.aspx']
def parse(self, response):
gsw_divs=response.xpath('//div[@class="sons"]/div[@class="cont"]')
# 过滤非正常诗文,并得取诗文详细内容链接
if gsw_divs:
for gsw_div in gsw_divs:
# time.sleep(0.1)
if gsw_div.xpath('./div[@class="yizhu"]'):
href = gsw_div.xpath('./p/a/@href').get()
href_url = response.urljoin(href)
yield scrapy.Request(url=href_url, callback=self.parse_1)
#翻页处理:
# next_href = response.xpath('//a[@id="amore"]/@href').get()
# # 翻页
# if next_href:
# next_url = response.urljoin(next_href) # urljoin()可以进行url地址的补全
# # request = scrapy.Request(next_url)
# # yield request
# yield scrapy.Request(
# url=next_url,
# callback=self.parse # 如果这个逻辑是这个parse 就可以省略
# )
#这里的response进入到详细页了
def parse_1(self,response):
#获取四项:名称...
html_text = response.xpath('//div[@id="sonsyuanwen"][1]/div[@class="cont"]')
title = html_text.xpath('./h1/text()').get()
author = html_text.xpath('./p[@class="source"]/a[1]/text()').get()
dynasty = html_text.xpath('./p[@class="source"]/a[2]/text()').get()
content_list = html_text.xpath('./div[@class="contson"]//text()').getall()
content = ''.join(content_list).strip()
#取得“展开阅读全文”链接,如果有则得取ajax页的译文和注释,没有则提取本页上的译文和注释
pd=response.xpath('//a[@style="text-decoration:none;"]/@href').get()
#'//div[@style="text-align:center; margin-top:-5px;"]/a/@href'
if pd:
# try:
ID=re.match(r"javascript:.*?,'(.*?)'",str(pd)).group(1)
# print(type(ID))
base_url='https://so.gushiwen.cn/nocdn/ajaxfanyi.aspx?id={}'
parse_html = etree.HTML(requests.get(base_url.format(ID)).text)
# print(parse_html)
html_fanyi= parse_html.xpath('//div[@class="contyishang"]//text()')
# print(html_fanyi) 测试用
# print('*'*10)
# except:
# pass
else:
html_fanyi = response.xpath(
'//div[@class="left"]/div[@class="sons"][2]/div[@class="contyishang"]//text()').getall()
# print(html_fanyi)
html_fanyi = ''.join(html_fanyi).replace('\n', '').replace('译文及注释', '').replace('译文', '').replace('注释', '|')
if html_fanyi:
fanyi = html_fanyi.split('|')
translation = fanyi[0]
notes = fanyi[1]
else:
translation = ''
notes = ''
item = PoemsItem(title=title,
author=author,
dynasty=dynasty,
content=content,
translation=translation,
notes=notes)
yield item
总结:
- URL一定要正确,一般scrapy不会出现乱码 如果出现了settings.py文件中加个配置项 FEED_EXPORT_ENCODING = 'utf-8
- 爬到好多空列表—很可能 xpath(就是爬取数据是否正确) 、解析是否正确、反爬、要登录
(13)
需求:爬取汽车之家图片
第一步 页面分析
https://car.autohome.com.cn/photolist/series/48114/6381346.html#pvareaid=3454450
https://car.autohome.com.cn/photolist/series/18/p2/ 第二页
https://car.autohome.com.cn/photolist/series/18/p3/ 第三页
每一个图片地址在//ul[@id=“imgList”]/li ./a/img/@src中
第二步 实现步骤
所有的图片在’//ul[@id=“imgList”]/li’中,一个在 './a/img/@src’中 可能会编辑items.py
piplines.py保存(两种方式)
根据以上操作,记得编辑settings.py文件
middlewares.py编辑
代码 D:\python_spider\基于Scrapy\pic1\audi
第一种方式(指保存方式的不同)
1.audi_spider.py
lass AudiSpiderSpider(scrapy.Spider):
name = 'audi_spider'
allowed_domains = ['car.autohome.com.cn']
start_urls = ['https://car.autohome.com.cn/photolist/series/18/p2/']
def parse(self, response):
lis = response.xpath('//ul[@id="imgList"]/li')
for li in lis:
# item = {}
item = AudiItem()
# 方式一
# item['src'] = 'https:' + li.xpath('./a/img/@src').extract_first()
# yield item
2.piplines.py
import os
from scrapy.pipelines.images import ImagesPipeline
class AudiPipeline:
def process_item(self, item, spider):
# 获取图片的url
src = item['src']
# 获取图片的名字
img_name = item['src'].split("__")[-1]
# 动态的添加这个路径(目录)
file_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'images')
print(img_name)
# request.urlretrieve专门用来保存二进制文件
request.urlretrieve(src, file_path + '/' + img_name)
return item
3.settings.py
ITEM_PIPELINES = {
'audi.pipelines.AudiPipeline': 300,
# 'scrapy.pipelines.images.ImagesPipeline':1 #固定写法
}
第二种方式
1.audi_spider.py
item['image_urls']=['https:' + li.xpath('./a/img/@src').extract_first()]
yield item
2.items.py
image_urls=scrapy.Field() #image_urls不能改
3.piplines.py不需编辑
4.settings.py
ITEM_PIPELINES = {
# 'audi.pipelines.AudiPipeline': 300,
'scrapy.pipelines.images.ImagesPipeline':1 #固定写法
}
import os
IMAGES_STORE = os.path.join(os.path.dirname(os.path.dirname(__file__)),'images') #IMAGES_STORE不能改