文章目录
两种方式爬取Hacker News网页
Hacker News网页是国外一个很受欢迎的新闻聚合网站,计算机科学家、企业家、数据科学家对此很感兴趣
使用requests和Beautiful Soup爬取
使用requests和Beautiful Soup爬取是一般的做法吗,以下代码将使用一个简单的Python字典对象列表存储爬取的信息
import requests
import re
from bs4 import BeautifulSoup
articles = []
url = 'https://news.ycombinator.com/news'
r = requests.get(url)
html_soup = BeautifulSoup(r.text, 'html.parser')
for item in html_soup.find_all('tr', class_='athing'):
item_a = item.find('a', class_='storylink')
item_link = item_a.get('href') if item_a else None
item_text = item_a.get_text(strip=True) if item_a else None
next_row = item.find_next_sibling('tr')
item_score = next_row.find('span', class_='score')
item_score = item_score.get_text(strip=True) if item_score else '0 points'
# We use regex here to find the correct element
item_comments = next_row.find('a', text=re.compile('\d+( |\s)comment(s?)'))
item_comments = item_comments.get_text(strip=True).replace('\xa0', ' ') \
if item_comments else '0 comments'
articles.append({
'link' : item_link,
'title' : item_text,
'score' : item_score,
'comments' : item_comments})
for article in articles:
print(article)
输出内容如下
使用api爬取
Hacker News有Api。提供结构化 、JSON格式的结果,下面是对上面示例代码的修改,使其不依赖于BS对html的解释
import requests
articles = []
url = 'https://hacker-news.firebaseio.com/v0'
top_stories = requests.get(url + '/topstories.json').json()
for story_id in top_stories:
story_url = url + '/item/{}.json'.format(story_id)
print('Fetching:', story_url)
r = requests.get(story_url)
story_dict = r.json()
articles.append(story_dict)
for article in articles:
print(article)
爬取书籍信息
使用requests和bs爬http://books.toscrape.com/上的书籍信息,网页显示内容如下
对于每本书,都需要获得
- 标题
- 封面
- 价格和库存情况
- 评级
- 产品说明
- 其他产品信息
然后使用dataset库将此信息存储在sqlite数据库中(以使用更新的方式编写程序,这样就可以在多次运行程序的情况下不会在数据库中插入重复记录)
核心代码如下
if __name__ == "__main__":
# Scrape the pages in the catalogue
url = base_url
inp = input('Do you wish to re-scrape the catalogue (y/n)? ')
while True and inp == 'y':
# 访问base_url拿到页面的链接
scrape_books(html_soup, url) # 分析url,拿到页面中的图书名和链接,存入到db数据库对应的表中
# Is there a next page?
next_a = html_soup.select('li.next > a') # 获取下一页
if not next_a or not next_a[0].get('href'):
break
url = urljoin(url, next_a[0].get('href'))
# Now scrape book by book, oldest first
books = db['books'].find(order_by=['last_seen']) # 从db中取出对应的图书,里面包含图书名和链接
for book in books:
# 访问图书具体的链接
scrape_book(html_soup, book_id) # 从图书链接中拿到图书的信息,存放到数据库db['books_info']中
# Update the last seen timestamp
db['books'].upsert({'book_id': book_id,
'last_seen': datetime.now()
}, ['book_id'])
爬取GitHub上项目被收藏的次数
咱们要爬取的链接最终地址为:https://github.com/Macuyiko?page=1&tab=repositories
如果是企业用户,则为:https://github.com/google?page=1&tab=repositories
要获取的信息,主要是项目名和编程语言、star个数
代码如下
#!/usr/bin/env python
# encoding: utf-8
import requests
from bs4 import BeautifulSoup
import re
session = requests.Session()
url = 'https://github.com/{}'
username = 'Macuyiko'
if __name__ == "__main__":
r = session.get(url.format(username), params={'page': 1, 'tab': 'repositories'}, verify=False)
html_soup = BeautifulSoup(r.text, 'html.parser')
is_normal_user = False
repos_element = html_soup.find(class_='repo-list') # 企业用户才会有repo-list,非企业用户为user-repositories-list
if not repos_element:
is_normal_user = True
repos_element = html_soup.find(id='user-repositories-list')
repos = repos_element.find_all('li')
for repo in repos:
name = repo.find('h3').find('a').get_text(strip=True) # 找到元素下的h3标签下的a标签,取出其text的内容
language = repo.find(attrs={'itemprop': 'programmingLanguage'}) # 找到itemprop属性等于programmingLanguage的标签
language = language.get_text(strip=True) if language else 'unknown'
stars = repo.find('a', attrs={'href': re.compile('\/stargazers')})
stars = int(stars.get_text(strip=True).replace(',', '')) if stars else 0
print(name, language, stars)
爬取和分析网络论坛的互动
爬取网络论坛的互动信息
爬取的目标网站内容如下,分为两个,第一个是主页的帖子列表
第二个为帖子中的回复信息,只取出对应的用户,不关注发表的内容
所以获取的核心就是首先拿到所有的标签信息,然后通过bs或者xpath来匹配,可以多拿几页数据
爬取的内容主要为评论和回复
对应的帖子内容为
分析网络论坛的互动信息
核心代码如下
heatmap = plt.pcolor(df, cmap='Blues')
y_vals = np.arange(0.5, len(df.index), 1)
x_vals = np.arange(0.5, len(df.columns), 1)
plt.yticks(y_vals, df.index)
plt.xticks(x_vals, df.columns, rotation='vertical')
for y in range(len(df.index)):
for x in range(len(df.columns)):
if df.iloc[y, x] == 0:
continue
plt.text(x + 0.5, y + 0.5, '%.0f' % df.iloc[y, x],
horizontalalignment='center',
verticalalignment='center')
plt.savefig("1.jpg")
plt.show()
整个部分的代码,posts此时保存的内容,每一个listitem为一个帖子,帖子的内容大概分为两种,非引用的(‘bluefish’, []);引用的(‘almostthere’, [‘kayman’])
在这个部分,需要把posts的内容进行切分,保存成此类型:‘zeke’: {‘bluefish’: 1, ‘almostthere’: 4}
所表单的意思为,zeke引用了两个人,分别为bluefish何almostthere,bluefish引用了一次,almostthere引用了4次;将切分处理后的内容保存到users中,内容处理完成后,大概如下
{'zeke': {'bluefish': 1, 'almostthere': 1}, 'trinity': {'almostthere': 1}, 'paula53': {'almostthere': 1}, 'toejam': {'almostthere': 1, 'Ohm': 1}, 'stickman': {'almostthere': 1}, 'tamtrails': {'almostthere': 1}, 'almostthere': {'tamtrails': 1, 'kayman': 1}, 'kayman': {'almostthere': 1}, 'lanceman': {'almostthere': 1}, 'pollock': {'almostthere': 1}, 'mitsmit': {'almostthere': 1}, 'Christian': {'almostthere': 1}, 'softskull': {'almostthere': 1}, 'argus': {'almostthere': 1}, 'lyssa7': {'almostthere': 1}, 'kevin': {'almostthere': 1}, 'dogrescuer': {'almostthere': 1}, 'RedDoug': {'kayman': 1}, 'Richard': {'almostthere': 1}, 'rebeccad': {'almostthere': 1, 'rangewalker': 2, 'Ohm': 1}, 'assen': {'almostthere': 1}, 'james2020': {'almostthere': 1}, 'gabby': {'rangewalker': 2, 'reuben': 1}, 'texasbb': {'rangewalker': 1}, 'High Sierra Fan': {'rangewalker': 1}, 'Ohm': {'rangewalker': 2, 'Lamebeaver': 1, 'reuben': 1, 'cheaptentguy': 1}, 'Lamebeaver': {'rangewalker': 1}, 'reuben': {'rangewalker': 2, 'Ohm': 1}, 'cheaptentguy': {'rangewalker': 1}}
将users转成pd.DataFrame的二维数组类型,那么index就是字典的key,columns就是value;再按照这个二维数组来遍历值然后输出到plt中,最后看到输出的图像如下
收集和聚类时尚数据集
这个例子中,使用Zalando(一个瑞典网上商店)来获取时尚产品的图片集合,并使用t-SNE对他们进行聚类
获取图片素材
网站的地址如下:https://www.zalando.co.uk/womens-clothing-dresses/
只需要把15页的图片下载下来做测试即可,代码可参见附件
对图片进行聚类分析
t-SNE的原理和推导如下:t-SNE原理与推导,特别适用于高维数据集(如图片)的可视化
用imread加载图片的时候,可能会遇到scipy.misc报错的问题
from scipy.misc import imread报错:ImportError: cannot import name imread
两种方法解决
- 将scipy降级到1.2.1版本(pip install scipy==1.2.1)
- 使用imageio.imread代替imread读取图片
- 使用pillow来读取图片
t-SNE代码执行的效果如图(只选择了20张来做测试):
Amazon评论的情感分析
获取评论数据
从Amazon网页中,比如
https://www.amazon.com/product-reviews/1449355730
从chrome分析,获取所有当前页评论的结果是post方式,提交的字段为:
拼接字段,上传到path中就可以拿到当前页的评论数据了,response的数据需要自己做解析
做评论的情感分析
对每次评论的情感进行评分,需要使用到vaderSentiment库,安装使用
pip install -U vaderSentiment
此时还需要用到nltk库,安装为
pip install -U nltk
对于单个句子,使用vaderSentiment库非常简单,如下是使用vaderSentiment库的测试代码
#!/usr/bin/env python
# encoding: utf-8
from nltk.sentiment.vader import SentimentIntensityAnalyzer
# import nltk
# nltk.download('vader_lexicon') # 只需要执行一次
if __name__ == "__main__":
analyzer = SentimentIntensityAnalyzer()
sentence = "I'm really happy with my pyrchase"
vs = analyzer.polarity_scores(sentence)
print(vs) # {'neg': 0.0, 'neu': 0.556, 'pos': 0.444, 'compound': 0.6115}
对于较长文本的情感分析,一种简单的方法是计算每个句子的情感得分,并将其平均到文本中的所有句子中,示例如下
#!/usr/bin/env python
# encoding: utf-8
from nltk.sentiment.vader import SentimentIntensityAnalyzer
# import nltk
# nltk.download('vader_lexicon') # 只需要执行一次,分析单个句子时候需要用到
# import nltk
# nltk.download('punkt') # 只需要执行一次,分析多个句子时候需要用到
from nltk import tokenize
if __name__ == "__main__":
analyzer = SentimentIntensityAnalyzer()
paragraph = """
I'm really happy with my pyrchase.
I've been using the producy for two weeks now.
It does exactly as described in the product description.
The only problem is that it takes a long time to charge.
However, since I recharge during nights,this is something I can live with.
"""
sentence_list = tokenize.sent_tokenize(paragraph)
cumulative_sentiment = 0.0
for sentence in sentence_list:
vs = analyzer.polarity_scores(sentence)
cumulative_sentiment += vs["compound"]
print(sentence, " : ", vs["compound"])
average_score = cumulative_sentiment / len(sentence_list)
print("Average score:", average_score)
输入如下
I'm really happy with my pyrchase. : 0.6115
I've been using the producy for two weeks now. : 0.0
It does exactly as described in the product description. : 0.0
The only problem is that it takes a long time to charge. : -0.4019
However, since I recharge during nights,this is something I can live with. : 0.0
Average score: 0.04192000000000001
如果遇到资源错误,又无法下载punkt的,请参阅参考链接中的“nltk.download(‘punkt’) False”
把这样的思路应用到上面获取到的Amazon评论当中,很容易得到每一个评论的情感,得到每一个评论的情感后再按照评价的星级进行归类,就可以得到每个星级的情感平均分了。matplotlib中的小提琴图,很容易能看到这些分布
爬取和分析维基百科关联图
数据获取
需要使用到两个数据库表
- 一个为pages:记录访问过的URL列表机器页面标题
- 另一个为links,仅仅包含一对url来表示页面之间的链接
爬取的时候使用joblib库来进行多线程
比较有意思的可能是这个页面发现链接了,使用了joblib模块,在模块的线程回调函数中执行get_title_and_links函数,该函数获取当前html的标题和当前url链接、页面发现的链接,然后到结果scraped_results中;一轮结束后外层函数遍历这个scraped_results,把当前html的标题和当前url链接放到store_page中处理,页面发现的链接在store_links中处理;
下一次的循环通过get_random_unvisited_pages返回,get_random_unvisited_pages主要完成从所以发现的链接中获取未访问的,main部分的代码如下
if __name__ == '__main__':
urls_to_visit = [base_url]
while urls_to_visit:
scraped_results = Parallel(n_jobs=5, backend="threading")(
delayed(get_title_and_links)(base_url, url) for url in urls_to_visit
)
for url, page_title, links in scraped_results:
store_page(url, page_title)
store_links(url, links)
urls_to_visit = get_random_unvisited_pages()
绘制关联图
有个爬取的数据,就可以使用NetworkX来可视化图了
有关NetworkX的使用不是重点,需要进一步了解的话,可以查看这个:networkx整理
爬取和可视化董事会成员图
爬取需要的数据
从网页https://de.reuters.com/finance/markets/index/.SPX中获取表格中的公司别名
得到别名后,拼接进链接中
officers = 'https://www.reuters.com/companies/{symbol}/people'
在链接中拿到对应公司的信息
然后存储到pandas中,并持久化到pickle中
可视化数据
使用NetworkX来简单地解析收集到的信息,并导出一种可以用Gephi(一个流行的图形可视化工具)读取的格式的图形,此工具可以从https://gephi.org/users/download/下载
不过比较失败的是,我没能按照书中的过滤得到最后的图
我的效果得到的是这个,还不清楚如何配置过滤选项
这是的用Gephi软件打开的.gexf文件
使用深度学习破解验证码图片
构建训练集
首先需要安装一些用到的模块
pip install -U captcha
pip install -U numpy
pip install -U opencv-python
生成4个字母长度的训练集
constants.py包含了一些变量
CAPTCHA_FOLDER = 'generated_images'
LETTERS_FOLDER = 'letters'
CHARACTERS = list('QWERTPASDFGHKLZXBNM')
NR_CAPTCHAS = 1000
NR_CHARACTERS = 4
MODEL_FILE = 'model.hdf5'
LABELS_FILE = 'labels.dat'
MODEL_SHAPE = (100, 100)
generate.py主要负责生成验证码
from random import choice
from captcha.image import ImageCaptcha
import os.path
from os import makedirs
from .constants import *
makedirs(CAPTCHA_FOLDER)
image = ImageCaptcha()
for i in range(NR_CAPTCHAS):
captcha = ''.join([choice(CHARACTERS) for c in range(NR_CHARACTERS)])
filename = os.path.join(CAPTCHA_FOLDER, '{}_{}.png'.format(captcha, i))
image.write(captcha, filename)
print('Generated:', captcha)
运行之后,可以看到在文件夹generated_images下出现如下的验证码
将图像分割成单独的部分,尝试构建模型
接下来的操作是把验证码图像分割成单独的部分,每个部分一个字符。使用opencv的对生成的图像进行阈值处理、开操作和轮廓检测。下面的代码主要做如下几个操作
- 将图片二值化后进行形态学操作,过滤掉噪声
- 用opencv的findContours方法提取链接的白色像素部分
- 调用drawContours来绘制发现的部分
- 将提取出来的轮廓按照文件夹名,分别存放到不同的文件夹中
下面是一个测试脚本,完成的功能就是提取一个字母,完成的操作如下
- 将原图像进行去噪得到图像1
- 创一个新的黑色图像,大小与原始图像一致
- 取出来一个轮廓,并用白色绘制出来
- 将图像1和蒙版按位and操作组合得到字母
import cv2
import numpy as np
# Change this to one of your generated images:
image_file = 'example.png'
image = cv2.imread(image_file)
cv2.imshow('Original image', image)
# Convert to grayscale, followed by thresholding to black and white
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
_, thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU)
cv2.imshow('Black and white', thresh)
# Apply opening: "erosion" followed by "dilation"
denoised = thresh.copy()
kernel = np.ones((4, 3), np.uint8)
denoised = cv2.erode(denoised, kernel, iterations=1)
kernel = np.ones((6, 3), np.uint8)
denoised = cv2.dilate(denoised, kernel, iterations=1)
cv2.imshow('Denoised', denoised)
# Now find contours and overlay them over our original image
_, cnts, _ = cv2.findContours(denoised.copy(), cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
contoured = image.copy()
cv2.drawContours(contoured, cnts, contourIdx=-1, color=(255, 0, 0), thickness=-1)
cv2.imshow('Contours', contoured)
# Create a fresh 'mask' image
mask = np.ones((image.shape[0], image.shape[1]), dtype="uint8") * 0
# We'll use the first contour as an example
contour = cnts[0]
# Draw this contour over the mask
cv2.drawContours(mask, [contour], -1, (255, 255, 255), -1)
cv2.imshow('Denoised image', denoised)
cv2.imshow('Mask after drawing contour', mask)
result = cv2.bitwise_and(denoised, mask)
cv2.imshow('Result after and operation', result)
retain = result > 0
result = result[np.ix_(retain.any(1), retain.any(0))]
cv2.imshow('Final result', result)
cv2.waitKey(0)
代码运行后得到的效果如下
如果按轮廓从左取到后,可以得到验证码的整个字符,但仍然需要考虑字符重叠;书中按照从最左边的白色像素到最右边的白色像素的距离除以期望看到的字符数(4)获取估计的宽度,如果轮廓比预期的要宽,将它切割成m个相等的部分,m等于轮廓的宽度除以预期的宽度
将思路封装到functions.py中,那么得到所有训练集的字符,只需要运行cut.py代码即可
使用深度学习框架
安装keras
安装tensorflow
pip install -U 其实安装tensorflow==1.9.0
安装配套版本的keras
pip install -U keras==2.2.0
keras和TensorFlow的版本需要匹配,当前我的tf版本是1.9.0,因此需要使用keras为2.2.0的版本,版本对应请参考:
https://docs.floydhub.com/guides/environments/
通过字符集来训练模型
要做的事情仅仅为
- 循环遍历创建的索引字符图像,调整他们的大小并存储他们的像素矩阵结果
- 数据进行规范化,使每个值都位于0~1之间
- 对字符进行二值化处理,每个标签都转换为输出定点,每个索引对应一个可能的字符,其值设置为1或者0,使类似Q的字母变成[1,0,0,0,…]
- 保存上面的转换,因为后期在模型的应用过程中还需要对字符进行逆转换
- 构建神经架构,开始训练模型
运行train.py代码后得到的输出如下
最终的两个输出文件为labels.dat(标签信息)和model.hdf5(模型)
测试识别结果
使用或者重新用一开始的代码生成一个新的验证码,放到test_images文件夹下作为测试
执行apply.py代码,可以看到如下结果,对于219中的图,比较难识别出来的HL还说得过去,可是连D都能识别成了A
全文所涉及的代码下载地址
https://download.csdn.net/download/zengraoli/12342255