前言
前面写过3篇文章,分别介绍了反爬措施,JS逆向+ajax获取数据,以及正则表达式匹配开头、结尾、中间的用法。第3篇算是本文 Python Selenium 爬虫实现方案的子集,大家可以参照阅读。
另外本意是“攻守”,不知道为何输入法给的都是“功守道”,前面没有注意全都写错了。已经纠正重新发布。
网站的反爬措施分析
JS逆向,ajax获取数据
正则表达式 - 匹配开头、结尾、中间
在这个爬虫案例中,我遇到的最难部分甚至都不是破解各种反爬措施和梳理网站逻辑,而是正则表达式的书写、测试和验证,在后者上耗费的心力远超前者。
js 逆向方案,只需要从 js 代码中匹配参数名、函数名,正则使用范围还相对有限。
而在本文提到的 Selenium 方案中,因为需要从页面的静态源代码中提取数据,而其中又夹杂了很多网站为反爬做的通过不同 css 样式包裹的假数据 —— 真中有假,假中有真。这样就需要做很多替换处理,而替换的前提正是匹配 —— 如何正确匹配,并且高效匹配 —— 是这个方案能够成功的关键。
正文
龙套登场
因为数据是经过渲染后才展示,直观感知就是页面打开后不是第一时间就能看到数据,以及通过浏览器查看的源代码中找不到数据。
无论标准的 request 还是 Scrapy 提供的 request,能够拿到的都只是这个找不到数据的网页源代码,像下面这样,只有包含样式的3个 Table,没有数据。巧妇难为无米之炊,没有米(数据),何来炊(提取)。
而Selenium 的不同(牛逼)之处是,他提供了1个属性 page_source,这个page_source 就能够拿到渲染后的页面源代码。我们可以把他保存到本地。如下图。可以看到每个Table 都已经填充了数据。
在 网站的反爬措施分析 这篇文章中,我们已经讲过网站针对 Selenium 做的反爬措施,在代码中都需要相应地解决掉。完整代码如下。
apath = r'F:\Python\Scrapy\S007_AQI\S007_AQI\.wdm\drivers\chromedriver\win32\108.0.5359\chromedriver.exe'
url = request.url
if 'daydata' in url:
city = request.meta['city']
month = request.meta['month']
options = webdriver.ChromeOptions()
options.add_argument(r'ignore-certificate-errors')
options.add_argument(r"--headless")
options.add_argument(
r'user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36')
options.add_experimental_option("excludeSwitches", ['enable-automation', 'enable-logging'])
driver = webdriver.Chrome(service=Service(apath), options=options)
# Selenium 模式也会被网站监测,所以需要在网站的监测代码执行前,先执行下面的代码调整navigator属性
# 使用 headless 无头模式的话,还需要配置 plugins
driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", {
"source": """
Object.defineProperty(navigator, 'webdriver', {
get: () => undefined
})
Object.defineProperty(navigator, 'plugins', {
get: () => [1, 2, 3, 4, 5],
})
"""
})
driver.get(url)
# 返回Selenium 浏览器的参数
# print("当前浏览器内置user-agent:", driver.execute_script('return navigator.userAgent'))
# print("当前浏览器内置user-agent-plugins:", driver.execute_script('return navigator.plugins.length'))
# print("当前浏览器内置user-agent-languages:", driver.execute_script('return navigator.languages'))
time.sleep(3)
# page_source 是渲染后的页面源代码,类型为字符串,包含了想要的数据
page_source = driver.page_source
driver.close()
# 为了便于对照,根据拿到 page_source 的时间,先保留1份原始文本,
response_time = time.strftime("%Y%m%d%H%M%S")
with open("{}_{}_{}_original.html".format(city, month, response_time), 'w', encoding='utf-8') as f:
f.write(page_source)
主角上阵
现在渲染前、后的网页源代码我们都已拿到,让我们告别龙套 Selenium,该是主角 正则表达式 大显身手的时候了。
前言中提到匹配是关键,其实匹配也有前提,就是要找到规律。好在这个案例中假数据规律都比较明显,我们一个个来看。
真table vs 假table
严格来讲,没有全部是假数据的 假table,也没有全部是真数据的 真table。对,他就是这么神奇和牛逼 。在所谓的“假Table” 中,也能找到和“真Table”一致的数据 —— 你可以打开浏览器访问页面,然后和源代码逐行逐列逐单元格进行数据比对,虽然很费功夫,但这确实是事实,你需要接受。
另1个非常明显的佐证是实现页面渲染的 js 代码,在这个名为 showTable 的函数中,入参 items是ajax 接口返回的 json 格式数据,通过 items.foreach 遍历,对3个 Table(3个不同 id)渲染。在每个Table的 td 标签中,都有包含了 假的Math.random() 和 真的item.天气字段 数据。而且出现的位置随机,字段随机。
假数据肯定是不会在页面展示的,他只是反爬措施的一部分。换句话说,用来迷惑或者拦截掉那些水平一般的爬虫 。我们当然可以找到控制展示的逻辑,然后逐个 td 匹配进而替换,但毫无疑问这样效率极低,没有必要直接在最小范围操作。即便这样做了,那些所谓“假Table”中剩下的真数据,怎么处理?—— 最终还是要回到 Table 范畴。
既然是这样,我们就直接在 Table 层面来找不同 —— 找到“真Table”,剔除“假Table”。
首先你会发现id 不同,但这个 id 是每次请求后都会变化的随机字符串,不能作为比较的凭证。
其次是class引用样式的名称不同,但他和 id 一样,也是随机变化的,即便他们对应的内容都是 display:none
所以接下来,我们就找到内容固定的 style 部分,能看出来2个 Table 包含了对 position 属性的设置,而另1个没有。没有设置的这颗独苗,你可以在再和页面做一遍数据比对,虽然多了些页面没展示的,但是页面展示的数据都在这里。对的,这就是我们的目标。
让我们请出这个前不见古人后不见来者的正则表达式。这里有2行额外的代码,用来将源代码中的 eval 部分(会转换为js 代码,可以看我的 js 逆向文章)替换为空,因为这部分内容太长了,长到令人发指,会严重影响后续匹配的效率和时间。
# 可以先移除Script 中的eval,否则太长了,移除后 size 会减少很多
# 正则匹配也是运算,应该尽可能减小匹配范围
regex_script = r"eval\(.*"
page_source = re.sub(regex_script, '', page_source)
# 调试需要,用于统计程序运行时间
s1_time_start = time.time()
# 样式中没有设置 position 的Table,就是包含真实数据的,保留。将另外2个 替换为空
# 一步到位的正则表达式
re_one_step = r"(?=)[\s\S])*?position[\s\S]*?(?<=<\/table>)"
page_source, count = re.subn(re_one_step, '', page_source)
print('{}_{}: {} fake tables have been removed within {}s'.format(city, month, count,time.time() - s1_time_start)))
# 调试需要,我们再保存1份剩下了 “真Table”的网页源代码
with open("{}_{}_{}_removefaketable.html".format(city, month, response_time), 'w', encoding='utf-8') as f:
f.write(page_source)
另外使用了 re.subn 函数而不是常见的 re.sub,re.subn 会返回1个元组,其中第1个元素是替换后的字符串,第2个元素是替换次数。便于我们观察调试信息。即使这里毫无疑问地只有2个Table会被替换。
真td vs 假td
现在我们拿到了包含真实数据的Table。重申:他只是包含,并不是全部,这很重要 。我们来看 2015-01-01 的数据,页面上在这行展示了9个数据,而Table 的 tr 中提供了19个 td,也就是19个数据。
19个数据,我们没有办法通过数字本身去识别真伪,对于爬虫来讲他们毫无意义。只有那些人类可读的 hidden 样式,以及那些很明显是随机字符串的样式名称,在默默诉说着什么。以页面上没有展示数字20和70为例,当你按下 Ctrl 同时点击那些随机字符串,会发现他们对应的真实样式,全部是 display:none。好了,电影《寒战》经典台词:“rule number 1” —— 用来剔除伪数据的第1条样式出炉。
但从19个甚至更多数据得到9个有效数据,rule number 1 还远远不够。我们还找到了剩下的3个 rule:
rule number 2: 直接就是 display:none
rule number 3: hidden-lg —— 这个很特别 lg 是大型设备上隐藏,参考阅读:Bootstrap 响应式实用工具
rule number 4: 直接就是 hidden
rule number 5: 没有了,hidden-xs 不是,hidden-sm 和 hidden-md 也不是 —— 就是这么牛逼,hidden 也区分不同的适用对象
正则表达式,请继续你的表演。
第1步,得到全部td。对于1份正常——纯真没有无邪的数据来说,td 数量应该是9的倍数 —— 因为页面上就展示9个字段,再结合天数,假设每天都有数据,那 td 不外乎288(31天 + 1表头),279(30天 + 1表头),161(28天 + 1表头)——即便不是每天都有数据,td 数量等于9的倍数这个铁律是没跑的。但实际在不同请求当中,因为随机伪数据的存在,得到的td 数量都是随机的。我们也把他打印出来。
第2步,在全部td 中,对前面得到的4种样式排名不分先后进行逐个匹配,匹配到就意味着属于将要被隐藏的数据,直接将这个td 替换为空。需要注意的是,对于名称是随机字符串的样式 —— rule number 1 来说,是先在源代码全文中匹配用{} 包含的display:none,然后提取出 { 前面的这个字符串,平均数量10个左右,你会得到1个列表,再然后在 td 中匹配这些字符串并替换 —— 这和 rule number 2 直接将 display:none 明文写在td样式中的处理逻辑不同。
第3步,可选,为了和第1步的结论呼应,我们再匹配1次td,得到td 的数量,以确认我们的处理逻辑是正确的。
# 在保留的 table 中匹配出所有的 td
regex_td = r""
tds = re.findall(regex_td, page_source)
print('{}_{}: total {} tds been found'.format(city, month, len(tds)))
# 对假数据的隐藏,网站使用了4种不同的css样式来控制,目前就找到4种。将包含这4种样式的 td 全部替换为空
# 第1种 直接就是display:none
# 第2种 使用了样式别名,名称是随机字符串,但内容固定是 display:none,需要通过内容反向拿到样式别名
# 第3种 hidden-lg - 这个很特别 lg 是大型设备上隐藏
# 第4种 hidden
regex_td_css1 = r"display:none"
for td in tds:
if re.findall(regex_td_css1, td):
page_source = page_source.replace(td, '')
# 将包含 display:none 隐藏样式的 td 其实就是假数据,替换为空
# display:none 是最终效果,实际在代码里是随机字符串,而且好几个,所以先找到 css 定义中内容为 display:none 的样式名称
regex_td_css2 = r"(.*?)\s*{\n\s*display: none;"
class_ndisplays = re.findall(regex_td_css2, page_source)
# print('class_ndisplays', len(class_ndisplays))
# 将包含这些样式的 td 替换为空
for class_ndisplay in class_ndisplays:
class_ndisplay = class_ndisplay.replace('.', '').strip()
for td in tds:
if re.findall(class_ndisplay, td):
page_source = page_source.replace(td, '')
# 将包含 hidden-lg 隐藏样式的 td 其实就是假数据,替换为空
regex_td_css3 = "hidden-lg"
for td in tds:
if re.findall(regex_td_css3, td):
page_source = page_source.replace(td, '')
# 将包含 hidden 隐藏样式的 td 其实就是假数据,替换为空
regex_td_css4 = r"\"hidden\""
for td in tds:
if re.findall(regex_td_css4, td):
page_source = page_source.replace(td, '')
# 可选,再次匹配,现在得到的是包含真实数据的 td
# 经过前面的处理,得到的有效td 数量应该固定为 (天数 + 表头)* 9列
# (31 + 1)*9 = 288 或者 (30+1)*9 = 279
regex_td = r""
tds_real = re.findall(regex_td, page_source)
print('{}_{}: {} tds which contain real data been left'.format(city, month, len(tds_real)))
终于!!!彻底!!!我们拿到了只剩真实数字(注意是数字!卖个关子,你还会继续发现惊喜和意外。哈哈哈哈哈 )的网页源代码!!!每一个 tr 都只有9条 td,可以再来和页面做个比对,数字完全一致。9真是这个案例的吉祥数字。细心的同学会发现两边数字的次序不同,对,因为图左是浏览器请求,图右是Python 代码请求。没错,每次请求拿到的数据顺序也是变化的。
到这里,我们把这个经过 Scrapy middleware 处理的网页源代码封装后发给 Scrapy Spider,在Spider 中做最后的数据提取。这里的提取我们用了 xpath
第1步,从页面中找出全部 tr,数量最多32个。
第2步,需要先从第1个 tr (也就是表头)中提取天气指标字段保存成列表,因为顺序不固定,所以每次都要提取,好让剩下的 tr 中的数字逐个对应到指标,这样最终的数据才有意义。因为只有1个 tr 需要这样处理,所以放在了 else 逻辑中,而用if 来处理绝大多数的 tr。
第3步,也就是 if 逻辑中,逐个提取数据,用指标列表作为key,提取到的数字作为 value,保存进字典 item.
其他一些额外的逻辑就是对表头的字符串处理,为了让不同的爬虫能套用同1套item 定义
def parse_month(self, response):
# 这里拿到的 response,应该是已经剔除掉全部伪数据的(包括表头伪数据)
# print(response.meta)
city = response.meta['city']
month = response.meta['month']
item = S007AqiItem()
# xpath 得到 tr,每个页面上 tr 最多32个 (31天 + 1表头)
trows = response.xpath('//tr')
print('{}_{}: {} rows been found'.format(city, month, len(trows)))
# 定义1个列表,用于储存表头字段
theaders_original = []
theaders_purified = []
# 循环,提取每个 tr 下的 td 中的数据
for trow, trow_count in zip(trows, list(range(0, len(trows)))):
# 每个 tr 里面包含 9个 td
tdatas = trow.xpath('./td')
# 提取剩余的31行
if trow_count != 0:
tdata_count = 0
for tdata in tdatas:
data = tdata.xpath('./text()').extract_first()
item['{}'.format(theaders_purified[tdata_count])] = data
tdata_count = tdata_count + 1
item['city'] = city
item['month'] = month
print(item)
# 第1个 tr 里面是表头,单独提取
else:
for tdata in tdatas:
field_name = tdata.xpath('./text()').extract_first()
theaders_original.append(field_name)
# 表头名称转换,变成英文小写,这样可以和使用ajax 方式的爬虫共用1个 item
if field_name == "日期": field_name = 'day'
elif field_name == "质量等级": field_name = 'quality'
elif field_name == "O3_8h": field_name = 'o3'
elif field_name == "PM2.5":field_name = 'pm2_5'
else: field_name = field_name.lower()
# 空列表不能直接使用数字索引赋值
theaders_purified.append(field_name)
print('{}_{}: original table headers : {}'.format(city, month, theaders_original))
print('{}_{}: purified table headers : {}'.format(city, month, theaders_purified))
真th vs 假th
前面红色字体处卖了个关子,拿到真实数字并不意味着万事大吉。为什么强调数字?—— 因为还有例外,就是表头。对,大部分情况下,表头也是写在 td 里的,这样只需要1套处理 td 的逻辑。但是哈但是,哈哈哈哈哈哈,你还会遇到表头写在 th 中的情况 —— 我没找着规律,似乎是在连续快速请求的情况下会出现。
为了让我们的爬虫更健壮,我们把这种情况也纳入考量。找到 th 是第1步,之后可以考虑在 th 中匹配 css rule 1-4,把假 th 剔除,但想想就闹心,难不成又来4次 for 循环? 所以换个思路,我们把所有的 th 替换为 td,放在td 处理逻辑前面,这样后续只处理 td 就好了。哈哈哈哈哈,你有张良计我有过墙梯。
# 表头也是包含了伪造数据的,也需要剔除 表头通过2种形式提供,
# 一种是放在 td 里的,可以和数据一起处理
# 另一种是放在 th 里,所以还需要增加 th 的匹配判断
# 保留下来的 Table 中, td 是一定有的,th 不一定有
# 最简单的方式就是把 th 替换为 td,这样就不用在 th 里依次判断4种隐藏样式
regex_th = r""
# 无论最终是否执行替换,正则匹配都会执行。所以把 re.findall 换成 re.subn
# 正则匹配后得到match,如果其中有闭合的(),就会生成 group,也就是分组
# 正则将之间的内容匹配出来,得到分组,用\1表示
# 将匹配到的分组内容保留,两端替换为td的开合标签
page_source, count = re.subn(regex_th, r"", page_source)
if count > 0:
print('{}_{}: field names are inside of , rather than . {} ths been replaced.'.format(city, month, count))
另类的 span
做完了前面的全部,你会发现结果里少了点什么,对,表头拿到了,数字拿到了,但是质量等级这个指标没有被提取出来 —— 为什么?回去看网页源代码,会发现质量等级是在 td 标签内部又嵌套了一层的 span 标签里。那好办,我们for 循环所有td,在td 里先找着 span 标签,然后提取其中的文本,用文本把这整个span 替换,这样把质量等级字段也处理成和其他字段一致的形式。
# 质量等级 是在 td 内部又包了1层 span 标签,为了和其他数据统一逻辑,我们把span 内的文本提取出来
# 然后用这个文本 把整个 span 标签替换掉
regex_span = r"" # 结果要包含开头的
regex_span_text = r"(?<=>).+(?=<)" # 结果不要包含开头的 < 和 结尾的 >, 只取<> 之间的内容
for td in tds:
span_tag = re.findall(regex_span, td)
if span_tag:
span_text = re.findall(regex_span_text, span_tag[0])[0]
page_source = page_source.replace(span_tag[0], span_text)
############################华丽丽的分割线##########################################
# 上面这个是最开始的写法,看上去就非常臃肿
# 下面这个是改良写法,看上去就非常牛逼
# 效果是把 质量等级
# 一步到位替换成了 质量等级
# \3 是因为质量等级在正则匹配结果的第3个分组
regex_span = r"(?=)[\s\S])*((.*?)<\/span>)<\/td>"
page_source, count = re.subn(regex_span , r' \3 ', page_source)
到这里,目前所有的意外都被我们处理掉了,数据 —— 所有的美好如期而至。无图无真相,来看个视频吧。视频被压缩得太厉害了,将就看吧。
结语
上面放的那个视频,我做了剪辑,中间砍掉了大概50s的样子。从这里也能看出 Selenium 方案的天然劣势:慢。除了 Selenium 自身之外,文本处理耗费了相当长的时间,有这功夫,js逆向 + ajax 请求方案至少爬10个页面吧。但这不失为1种可行方案 —— 在熟悉和熟练掌握正则表达式的前提下。
Todo
在现在这个基础上,还能想到的几个时长优化思路:
第1:4个隐藏样式匹配那里,对全部td做了4次循环,而且是用了 re.findall 然后 replace。撇开 rule number 1 需要逆向找到字符串再处理外,其他3个rule 其实可以考虑使用 re.subn(regex_rule2 | regex_rule3 | regex_rule4 , '', td) —— 1次性匹配3个正则其中1个,同时替换,这样可以减少2次 for 循环,而且代码简洁度大大提升。
第2:简化 span 的处理逻辑,新的代码已经放上去了,7行代码精简成了2行,但是正则表达式更牛逼 —— 复杂了。
这2个思路共通的地方是 将 re.findall + for 循环 + replace ,替换为 re.sub。 区别在于前面这个方案是不断缩小范围,后面这个是直接在大范围内(其实也可以改小)。总的来说是 replace 可以被干掉。但是实际性能和效率对比如何,待进一步考证吧。
你可能感兴趣的:(python,python,爬虫,selenium)
理解Gunicorn:Python WSGI服务器的基石
范范0825
ipython linux 运维
理解Gunicorn:PythonWSGI服务器的基石介绍Gunicorn,全称GreenUnicorn,是一个为PythonWSGI(WebServerGatewayInterface)应用设计的高效、轻量级HTTP服务器。作为PythonWeb应用部署的常用工具,Gunicorn以其高性能和易用性著称。本文将介绍Gunicorn的基本概念、安装和配置,帮助初学者快速上手。1.什么是Gunico
Python数据分析与可视化实战指南
William数据分析
python python 数据
在数据驱动的时代,Python因其简洁的语法、强大的库生态系统以及活跃的社区,成为了数据分析与可视化的首选语言。本文将通过一个详细的案例,带领大家学习如何使用Python进行数据分析,并通过可视化来直观呈现分析结果。一、环境准备1.1安装必要库在开始数据分析和可视化之前,我们需要安装一些常用的库。主要包括pandas、numpy、matplotlib和seaborn等。这些库分别用于数据处理、数学
python os.environ
江湖偌大
python 深度学习
os.environ['TF_CPP_MIN_LOG_LEVEL']='0'#默认值,输出所有信息os.environ['TF_CPP_MIN_LOG_LEVEL']='1'#屏蔽通知信息(INFO)os.environ['TF_CPP_MIN_LOG_LEVEL']='2'#屏蔽通知信息和警告信息(INFO\WARNING)os.environ['TF_CPP_MIN_LOG_LEVEL']='
Python中os.environ基本介绍及使用方法
鹤冲天Pro
# Python python 服务器 开发语言
文章目录python中os.environos.environ简介os.environ进行环境变量的增删改查python中os.environ的使用详解1.简介2.key字段详解2.1常见key字段3.os.environ.get()用法4.环境变量的增删改查和判断是否存在4.1新增环境变量4.2更新环境变量4.3获取环境变量4.4删除环境变量4.5判断环境变量是否存在python中os.envi
Pyecharts数据可视化大屏:打造沉浸式数据分析体验
我的运维人生
信息可视化 数据分析 数据挖掘 运维开发 技术共享
Pyecharts数据可视化大屏:打造沉浸式数据分析体验在当今这个数据驱动的时代,如何将海量数据以直观、生动的方式展现出来,成为了数据分析师和企业决策者关注的焦点。Pyecharts,作为一款基于Python的开源数据可视化库,凭借其丰富的图表类型、灵活的配置选项以及高度的定制化能力,成为了构建数据可视化大屏的理想选择。本文将深入探讨如何利用Pyecharts打造数据可视化大屏,并通过实际代码案例
Python教程:一文了解使用Python处理XPath
旦莫
Python进阶 python 开发语言
目录1.环境准备1.1安装lxml1.2验证安装2.XPath基础2.1什么是XPath?2.2XPath语法2.3示例XML文档3.使用lxml解析XML3.1解析XML文档3.2查看解析结果4.XPath查询4.1基本路径查询4.2使用属性查询4.3查询多个节点5.XPath的高级用法5.1使用逻辑运算符5.2使用函数6.实战案例6.1从网页抓取数据6.1.1安装Requests库6.1.2代
python os.environ_python os.environ 读取和设置环境变量
weixin_39605414
python os.environ
>>>importos>>>os.environ.keys()['LC_NUMERIC','GOPATH','GOROOT','GOBIN','LESSOPEN','SSH_CLIENT','LOGNAME','USER','HOME','LC_PAPER','PATH','DISPLAY','LANG','TERM','SHELL','J2REDIR','LC_MONETARY','QT_QPA
使用Faiss进行高效相似度搜索
llzwxh888
faiss python
在现代AI应用中,快速和高效的相似度搜索是至关重要的。Faiss(FacebookAISimilaritySearch)是一个专门用于快速相似度搜索和聚类的库,特别适用于高维向量。本文将介绍如何使用Faiss来进行相似度搜索,并结合Python代码演示其基本用法。什么是Faiss?Faiss是一个由FacebookAIResearch团队开发的开源库,主要用于高维向量的相似性搜索和聚类。Faiss
python是什么意思中文-在python中%是什么意思
编程大乐趣
Python中%有两种:1、数值运算:%代表取模,返回除法的余数。如:>>>7%212、%操作符(字符串格式化,stringformatting),说明如下:%[(name)][flags][width].[precision]typecode(name)为命名flags可以有+,-,''或0。+表示右对齐。-表示左对齐。''为一个空格,表示在正数的左侧填充一个空格,从而与负数对齐。0表示使用0填
Day1笔记-Python简介&标识符和关键字&输入输出
~在杰难逃~
Python python 开发语言 大数据 数据分析 数据挖掘
大家好,从今天开始呢,杰哥开展一个新的专栏,当然,数据分析部分也会不定时更新的,这个新的专栏主要是讲解一些Python的基础语法和知识,帮助0基础的小伙伴入门和学习Python,感兴趣的小伙伴可以开始认真学习啦!一、Python简介【了解】1.计算机工作原理编程语言就是用来定义计算机程序的形式语言。我们通过编程语言来编写程序代码,再通过语言处理程序执行向计算机发送指令,让计算机完成对应的工作,编程
python八股文面试题分享及解析(1)
Shawn________
python
#1.'''a=1b=2不用中间变量交换a和b'''#1.a=1b=2a,b=b,aprint(a)print(b)结果:21#2.ll=[]foriinrange(3):ll.append({'num':i})print(11)结果:#[{'num':0},{'num':1},{'num':2}]#3.kk=[]a={'num':0}foriinrange(3):#0,12#可变类型,不仅仅改变
每日算法&面试题,大厂特训二十八天——第二十天(树)
肥学
⚡算法题⚡面试题每日精进 java 算法 数据结构
目录标题导读算法特训二十八天面试题点击直接资料领取导读肥友们为了更好的去帮助新同学适应算法和面试题,最近我们开始进行专项突击一步一步来。上一期我们完成了动态规划二十一天现在我们进行下一项对各类算法进行二十八天的一个小总结。还在等什么快来一起肥学进行二十八天挑战吧!!特别介绍小白练手专栏,适合刚入手的新人欢迎订阅编程小白进阶python有趣练手项目里面包括了像《机器人尬聊》《恶搞程序》这样的有趣文章
Python快速入门 —— 第三节:类与对象
孤华暗香
Python快速入门 python 开发语言
第三节:类与对象目标:了解面向对象编程的基础概念,并学会如何定义类和创建对象。内容:类与对象:定义类:class关键字。类的构造函数:__init__()。类的属性和方法。对象的创建与使用。示例:classStudent:def__init__(self,name,age,major):self.name
pyecharts——绘制柱形图折线图
2224070247
信息可视化 python java 数据可视化
一、pyecharts概述自2013年6月百度EFE(ExcellentFrontEnd)数据可视化团队研发的ECharts1.0发布到GitHub网站以来,ECharts一直备受业界权威的关注并获得广泛好评,成为目前成熟且流行的数据可视化图表工具,被应用到诸多数据可视化的开发领域。Python作为数据分析领域最受欢迎的语言,也加入ECharts的使用行列,并研发出方便Python开发者使用的数据
Python 实现图片裁剪(附代码) | Python工具
剑客阿良_ALiang
前言本文提供将图片按照自定义尺寸进行裁剪的工具方法,一如既往的实用主义。环境依赖ffmpeg环境安装,可以参考我的另一篇文章:windowsffmpeg安装部署_阿良的博客-CSDN博客本文主要使用到的不是ffmpeg,而是ffprobe也在上面这篇文章中的zip包中。ffmpy安装:pipinstallffmpy-ihttps://pypi.douban.com/simple代码不废话了,上代码
【华为OD技术面试真题 - 技术面】- python八股文真题题库(4)
算法大师
华为od 面试 python
华为OD面试真题精选专栏:华为OD面试真题精选目录:2024华为OD面试手撕代码真题目录以及八股文真题目录文章目录华为OD面试真题精选**1.Python中的`with`**用途和功能自动资源管理示例:文件操作上下文管理协议示例代码工作流程解析优点2.\_\_new\_\_和**\_\_init\_\_**区别__new____init__区别总结3.**切片(Slicing)操作**基本切片语法
python os 环境变量
CV矿工
python 开发语言 numpy
环境变量:环境变量是程序和操作系统之间的通信方式。有些字符不宜明文写进代码里,比如数据库密码,个人账户密码,如果写进自己本机的环境变量里,程序用的时候通过os.environ.get()取出来就行了。os.environ是一个环境变量的字典。环境变量的相关操作importos"""设置/修改环境变量:os.environ[‘环境变量名称’]=‘环境变量值’#其中key和value均为string类
Python爬虫解析工具之xpath使用详解
eqa11
python 爬虫 开发语言
文章目录Python爬虫解析工具之xpath使用详解一、引言二、环境准备1、插件安装2、依赖库安装三、xpath语法详解1、路径表达式2、通配符3、谓语4、常用函数四、xpath在Python代码中的使用1、文档树的创建2、使用xpath表达式3、获取元素内容和属性五、总结Python爬虫解析工具之xpath使用详解一、引言在Python爬虫开发中,数据提取是一个至关重要的环节。xpath作为一门
【华为OD技术面试真题 - 技术面】- python八股文真题题库(1)
算法大师
华为od 面试 python
华为OD面试真题精选专栏:华为OD面试真题精选目录:2024华为OD面试手撕代码真题目录以及八股文真题目录文章目录华为OD面试真题精选1.数据预处理流程数据预处理的主要步骤工具和库2.介绍线性回归、逻辑回归模型线性回归(LinearRegression)模型形式:关键点:逻辑回归(LogisticRegression)模型形式:关键点:参数估计与评估:3.python浅拷贝及深拷贝浅拷贝(Shal
nosql数据库技术与应用知识点
皆过客,揽星河
NoSQL nosql 数据库 大数据 数据分析 数据结构 非关系型数据库
Nosql知识回顾大数据处理流程数据采集(flume、爬虫、传感器)数据存储(本门课程NoSQL所处的阶段)Hdfs、MongoDB、HBase等数据清洗(入仓)Hive等数据处理、分析(Spark、Flink等)数据可视化数据挖掘、机器学习应用(Python、SparkMLlib等)大数据时代存储的挑战(三高)高并发(同一时间很多人访问)高扩展(要求随时根据需求扩展存储)高效率(要求读写速度快)
《Python数据分析实战终极指南》
xjt921122
python 数据分析 开发语言
对于分析师来说,大家在学习Python数据分析的路上,多多少少都遇到过很多大坑**,有关于技能和思维的**:Excel已经没办法处理现有的数据量了,应该学Python吗?找了一大堆Python和Pandas的资料来学习,为什么自己动手就懵了?跟着比赛类公开数据分析案例练了很久,为什么当自己面对数据需求还是只会数据处理而没有分析思路?学了对比、细分、聚类分析,也会用PEST、波特五力这类分析法,为啥
Python中深拷贝与浅拷贝的区别
yuxiaoyu.
转自:http://blog.csdn.net/u014745194/article/details/70271868定义:在Python中对象的赋值其实就是对象的引用。当创建一个对象,把它赋值给另一个变量的时候,python并没有拷贝这个对象,只是拷贝了这个对象的引用而已。浅拷贝:拷贝了最外围的对象本身,内部的元素都只是拷贝了一个引用而已。也就是,把对象复制一遍,但是该对象中引用的其他对象我不复
Python开发常用的三方模块如下:
换个网名有点难
python 开发语言
Python是一门功能强大的编程语言,拥有丰富的第三方库,这些库为开发者提供了极大的便利。以下是100个常用的Python库,涵盖了多个领域:1、NumPy,用于科学计算的基础库。2、Pandas,提供数据结构和数据分析工具。3、Matplotlib,一个绘图库。4、Scikit-learn,机器学习库。5、SciPy,用于数学、科学和工程的库。6、TensorFlow,由Google开发的开源机
Python编译器
鹿鹿~
Python编译器 Python python 开发语言 后端
嘿嘿嘿我又来了啊有些小盆友可能不知道Python其实是有编译器的,也就是PyCharm。你们可能会问到这个是干嘛的又不可以吃也不可以穿好像没有什么用,其实你还说对了这个还真的不可以吃也不可以穿,但是它用来干嘛的呢。用来编译你所打出的代码进行运行(可能这里说的有点不对但是只是个人认为)现在我们来说说PyCharm是用来干嘛的。PyCharm是一种PythonIDE,带有一整套可以帮助用户在使用Pyt
一文掌握python面向对象魔术方法(二)
程序员neil
python python 开发语言
接上篇:一文掌握python面向对象魔术方法(一)-CSDN博客目录六、迭代和序列化:1、__iter__(self):定义迭代器,使得类可以被for循环迭代。2、__getitem__(self,key):定义索引操作,如obj[key]。3、__setitem__(self,key,value):定义赋值操作,如obj[key]=value。4、__delitem__(self,key):定义
一文掌握python常用的list(列表)操作
程序员neil
python python 开发语言
目录一、创建列表1.直接创建列表:2.使用list()构造器3.使用列表推导式4.创建空列表二、访问列表元素1.列表支持通过索引访问元素,索引从0开始:2.还可以使用切片操作访问列表的一部分:三、修改列表元素四、添加元素1.append():在末尾添加元素2.insert():在指定位置插入元素五、删除元素1.del:删除指定位置的元素2.remove():删除指定值的第一个匹配项3.pop():
Python实现简单的机器学习算法
master_chenchengg
python python 办公效率 python开发 IT
Python实现简单的机器学习算法开篇:初探机器学习的奇妙之旅搭建环境:一切从安装开始必备工具箱第一步:安装Anaconda和JupyterNotebook小贴士:如何配置Python环境变量算法初体验:从零开始的Python机器学习线性回归:让数据说话数据准备:从哪里找数据编码实战:Python实现线性回归模型评估:如何判断模型好坏逻辑回归:从分类开始理论入门:什么是逻辑回归代码实现:使用skl
python中的深拷贝与浅拷贝
anshejd70787
python
深拷贝和浅拷贝浅拷贝的时候,修改原来的对象,浅拷贝的对象不会发生改变。1、对象的赋值对象的赋值实际上是对象之间的引用:当创建一个对象,然后将这个对象赋值给另外一个变量的时候,python并没有拷贝这个对象,而只是拷贝了这个对象的引用。当对对象做赋值或者是参数传递或者作为返回值的时候,总是传递原始对象的引用,而不是一个副本。如下所示:>>>aList=["kel","abc",123]>>>bLis
用Python实现简单的猜数字游戏
程序媛了了
python 游戏 java
猜数字游戏代码:importrandomdefpythonit():a=random.randint(1,100)n=int(input("输入你猜想的数字:"))whilen!=a:ifn>a:print("很遗憾,猜大了")n=int(input("请再次输入你猜想的数字:"))elifna::如果玩家猜的数字n大于随机数字a,则输出"很遗憾,猜大了",并提示玩家再次输入。elifn
用Python实现读取统计单词个数
程序媛了了
python 游戏 java
完整实例代码:fromcollectionsimportCounterdefpythonit():danci={}withopen("pythonit.txt","r",encoding="utf-8")asf:foriinf:words=i.strip().split()forwordinwords:ifwordnotindanci:danci[word]=1else:danci[word]+=
[星球大战]阿纳金的背叛
comsci
本来杰迪圣殿的长老是不同意让阿纳金接受训练的.........
但是由于政治原因,长老会妥协了...这给邪恶的力量带来了机会
所以......现代的地球联邦接受了这个教训...绝对不让某些年轻人进入学院
看懂它,你就可以任性的玩耍了!
aijuans
JavaScript
javascript作为前端开发的标配技能,如果不掌握好它的三大特点:1.原型 2.作用域 3. 闭包 ,又怎么可以说你学好了这门语言呢?如果标配的技能都没有撑握好,怎么可以任性的玩耍呢?怎么验证自己学好了以上三个基本点呢,我找到一段不错的代码,稍加改动,如果能够读懂它,那么你就可以任性了。
function jClass(b
Java常用工具包 Jodd
Kai_Ge
java jodd
Jodd 是一个开源的 Java 工具集, 包含一些实用的工具类和小型框架。简单,却很强大! 写道 Jodd = Tools + IoC + MVC + DB + AOP + TX + JSON + HTML < 1.5 Mb
Jodd 被分成众多模块,按需选择,其中
工具类模块有:
jodd-core &nb
SpringMvc下载
120153216
springMVC
@RequestMapping(value = WebUrlConstant.DOWNLOAD)
public void download(HttpServletRequest request,HttpServletResponse response,String fileName) {
OutputStream os = null;
InputStream is = null;
Python 标准异常总结
2002wmj
python
Python标准异常总结
AssertionError 断言语句(assert)失败 AttributeError 尝试访问未知的对象属性 EOFError 用户输入文件末尾标志EOF(Ctrl+d) FloatingPointError 浮点计算错误 GeneratorExit generator.close()方法被调用的时候 ImportError 导入模块失
SQL函数返回临时表结构的数据用于查询
357029540
SQL Server
这两天在做一个查询的SQL,这个SQL的一个条件是通过游标实现另外两张表查询出一个多条数据,这些数据都是INT类型,然后用IN条件进行查询,并且查询这两张表需要通过外部传入参数才能查询出所需数据,于是想到了用SQL函数返回值,并且也这样做了,由于是返回多条数据,所以把查询出来的INT类型值都拼接为了字符串,这时就遇到问题了,在查询SQL中因为条件是INT值,SQL函数的CAST和CONVERST都
java 时间格式化 | 比较大小| 时区 个人笔记
7454103
java eclipse tomcat c MyEclipse
个人总结! 不当之处多多包含!
引用 1.0 如何设置 tomcat 的时区:
位置:(catalina.bat---JAVA_OPTS 下面加上)
set JAVA_OPT
时间获取Clander的用法
adminjun
Clander 时间
/**
* 得到几天前的时间
* @param d
* @param day
* @return
*/
public static Date getDateBefore(Date d,int day){
Calend
JVM初探与设置
aijuans
java
JVM是Java Virtual Machine(Java虚拟机)的缩写,JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。Java虚拟机包括一套字节码指令集、一组寄存器、一个栈、一个垃圾回收堆和一个存储方法域。 JVM屏蔽了与具体操作系统平台相关的信息,使Java程序只需生成在Java虚拟机上运行的目标代码(字节码),就可以在多种平台
SQL中ON和WHERE的区别
avords
SQL中ON和WHERE的区别
数据库在通过连接两张或多张表来返回记录时,都会生成一张中间的临时表,然后再将这张临时表返回给用户。 www.2cto.com 在使用left jion时,on和where条件的区别如下: 1、 on条件是在生成临时表时使用的条件,它不管on中的条件是否为真,都会返回左边表中的记录。
说说自信
houxinyou
工作 生活
自信的来源分为两种,一种是源于实力,一种源于头脑.实力是一个综合的评定,有自身的能力,能利用的资源等.比如我想去月亮上,要身体素质过硬,还要有飞船等等一系列的东西.这些都属于实力的一部分.而头脑不同,只要你头脑够简单就可以了!同样要上月亮上,你想,我一跳,1米,我多跳几下,跳个几年,应该就到了!什么?你说我会往下掉?你笨呀你!找个东西踩一下不就行了吗?
无论工作还
WEBLOGIC事务超时设置
bijian1013
weblogic jta 事务超时
系统中统计数据,由于调用统计过程,执行时间超过了weblogic设置的时间,提示如下错误:
统计数据出错!
原因:The transaction is no longer active - status: 'Rolling Back. [Reason=weblogic.transaction.internal
两年已过去,再看该如何快速融入新团队
bingyingao
java 互联网 融入 架构 新团队
偶得的空闲,翻到了两年前的帖子
该如何快速融入一个新团队,有所感触,就记下来,为下一个两年后的今天做参考。
时隔两年半之后的今天,再来看当初的这个博客,别有一番滋味。而我已经于今年三月份离开了当初所在的团队,加入另外的一个项目组,2011年的这篇博客之后的时光,我很好的融入了那个团队,而直到现在和同事们关系都特别好。大家在短短一年半的时间离一起经历了一
【Spark七十七】Spark分析Nginx和Apache的access.log
bit1129
apache
Spark分析Nginx和Apache的access.log,第一个问题是要对Nginx和Apache的access.log文件进行按行解析,按行解析就的方法是正则表达式:
Nginx的access.log解析正则表达式
val PATTERN = """([^ ]*) ([^ ]*) ([^ ]*) (\\[.*\\]) (\&q
Erlang patch
bookjovi
erlang
Totally five patchs committed to erlang otp, just small patchs.
IMO, erlang really is a interesting programming language, I really like its concurrency feature.
but the functional programming style
log4j日志路径中加入日期
bro_feng
java log4j
要用log4j使用记录日志,日志路径有每日的日期,文件大小5M新增文件。
实现方式
log4j:
<appender name="serviceLog"
class="org.apache.log4j.RollingFileAppender">
<param name="Encoding" v
读《研磨设计模式》-代码笔记-桥接模式
bylijinnan
java 设计模式
声明: 本文只为方便我个人查阅和理解,详细的分析以及源代码请移步 原作者的博客http://chjavach.iteye.com/
/**
* 个人觉得关于桥接模式的例子,蜡笔和毛笔这个例子是最贴切的:http://www.cnblogs.com/zhenyulu/articles/67016.html
* 笔和颜色是可分离的,蜡笔把两者耦合在一起了:一支蜡笔只有一种
windows7下SVN和Eclipse插件安装
chenyu19891124
eclipse插件
今天花了一天时间弄SVN和Eclipse插件的安装,今天弄好了。svn插件和Eclipse整合有两种方式,一种是直接下载插件包,二种是通过Eclipse在线更新。由于之前Eclipse版本和svn插件版本有差别,始终是没装上。最后在网上找到了适合的版本。所用的环境系统:windows7JDK:1.7svn插件包版本:1.8.16Eclipse:3.7.2工具下载地址:Eclipse下在地址:htt
[转帖]工作流引擎设计思路
comsci
设计模式 工作 应用服务器 workflow 企业应用
作为国内的同行,我非常希望在流程设计方面和大家交流,刚发现篇好文(那么好的文章,现在才发现,可惜),关于流程设计的一些原理,个人觉得本文站得高,看得远,比俺的文章有深度,转载如下
=================================================================================
自开博以来不断有朋友来探讨工作流引擎该如何
Linux 查看内存,CPU及硬盘大小的方法
daizj
linux cpu 内存 硬盘 大小
一、查看CPU信息的命令
[root@R4 ~]# cat /proc/cpuinfo |grep "model name" && cat /proc/cpuinfo |grep "physical id"
model name : Intel(R) Xeon(R) CPU X5450 @ 3.00GHz
model name :
linux 踢出在线用户
dongwei_6688
linux
两个步骤:
1.用w命令找到要踢出的用户,比如下面:
[root@localhost ~]# w
18:16:55 up 39 days, 8:27, 3 users, load average: 0.03, 0.03, 0.00
USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
放手吧,就像不曾拥有过一样
dcj3sjt126com
内容提要:
静悠悠编著的《放手吧就像不曾拥有过一样》集结“全球华语世界最舒缓心灵”的精华故事,触碰生命最深层次的感动,献给全世界亿万读者。《放手吧就像不曾拥有过一样》的作者衷心地祝愿每一位读者都给自己一个重新出发的理由,将那些令你痛苦的、扛起的、背负的,一并都放下吧!把憔悴的面容换做一种清淡的微笑,把沉重的步伐调节成春天五线谱上的音符,让自己踏着轻快的节奏,在人生的海面上悠然漂荡,享受宁静与
php二进制安全的含义
dcj3sjt126com
PHP
PHP里,有string的概念。
string里,每个字符的大小为byte(与PHP相比,Java的每个字符为Character,是UTF8字符,C语言的每个字符可以在编译时选择)。
byte里,有ASCII代码的字符,例如ABC,123,abc,也有一些特殊字符,例如回车,退格之类的。
特殊字符很多是不能显示的。或者说,他们的显示方式没有标准,例如编码65到哪儿都是字母A,编码97到哪儿都是字符
Linux下禁用T440s,X240的一体化触摸板(touchpad)
gashero
linux ThinkPad 触摸板
自打1月买了Thinkpad T440s就一直很火大,其中最让人恼火的莫过于触摸板。
Thinkpad的经典就包括用了小红点(TrackPoint)。但是小红点只能定位,还是需要鼠标的左右键的。但是自打T440s等开始启用了一体化触摸板,不再有实体的按键了。问题是要是好用也行。
实际使用中,触摸板一堆问题,比如定位有抖动,以及按键时会有飘逸。这就导致了单击经常就
graph_dfs
hcx2013
Graph
package edu.xidian.graph;
class MyStack {
private final int SIZE = 20;
private int[] st;
private int top;
public MyStack() {
st = new int[SIZE];
top = -1;
}
public void push(i
Spring4.1新特性——Spring核心部分及其他
jinnianshilongnian
spring 4.1
目录
Spring4.1新特性——综述
Spring4.1新特性——Spring核心部分及其他
Spring4.1新特性——Spring缓存框架增强
Spring4.1新特性——异步调用和事件机制的异常处理
Spring4.1新特性——数据库集成测试脚本初始化
Spring4.1新特性——Spring MVC增强
Spring4.1新特性——页面自动化测试框架Spring MVC T
配置HiveServer2的安全策略之自定义用户名密码验证
liyonghui160com
具体从网上看
http://doc.mapr.com/display/MapR/Using+HiveServer2#UsingHiveServer2-ConfiguringCustomAuthentication
LDAP Authentication using OpenLDAP
Setting
一位30多的程序员生涯经验总结
pda158
编程 工作 生活 咨询
1.客户在接触到产品之后,才会真正明白自己的需求。
这是我在我的第一份工作上面学来的。只有当我们给客户展示产品的时候,他们才会意识到哪些是必须的。给出一个功能性原型设计远远比一张长长的文字表格要好。 2.只要有充足的时间,所有安全防御系统都将失败。
安全防御现如今是全世界都在关注的大课题、大挑战。我们必须时时刻刻积极完善它,因为黑客只要有一次成功,就可以彻底打败你。 3.
分布式web服务架构的演变
自由的奴隶
linux Web 应用服务器 互联网
最开始,由于某些想法,于是在互联网上搭建了一个网站,这个时候甚至有可能主机都是租借的,但由于这篇文章我们只关注架构的演变历程,因此就假设这个时候已经是托管了一台主机,并且有一定的带宽了,这个时候由于网站具备了一定的特色,吸引了部分人访问,逐渐你发现系统的压力越来越高,响应速度越来越慢,而这个时候比较明显的是数据库和应用互相影响,应用出问题了,数据库也很容易出现问题,而数据库出问题的时候,应用也容易
初探Druid连接池之二——慢SQL日志记录
xingsan_zhang
日志 连接池 druid 慢SQL
由于工作原因,这里先不说连接数据库部分的配置,后面会补上,直接进入慢SQL日志记录。
1.applicationContext.xml中增加如下配置:
<bean abstract="true" id="mysql_database" class="com.alibaba.druid.pool.DruidDataSourc