Python商业数据挖掘实战——爬取网页并将其转为Markdown_爬取网页的markdown格式

    self._markdown = ''

def convert(self, html, options=None):
    elements = []
    for tag, pattern in BlOCK_ELEMENTS.items():
        for m in re.finditer(pattern, html, re.I | re.S | re.M):
            element = Element(start_pos=m.start(),
                              end_pos=m.end(),
                              content=''.join(m.groups()),
                              tag=tag,
                              is_block=True)
            can_append = True
            for e in elements:
                if e.start_pos < m.start() and e.end_pos > m.end():
                    can_append = False
                elif e.start_pos > m.start() and e.end_pos < m.end():
                    elements.remove(e)
            if can_append:
                elements.append(element)

    elements.sort(key=lambda element: element.start_pos)
    self._markdown = ''.join([str(e) for e in elements])

    for index, element in enumerate(DELETE_ELEMENTS):
        self._markdown = re.sub(element, '', self._markdown)
    return self._markdown

@property
def markdown(self):
    self.convert(self.html, self.options)
    return self._markdown

_inst = Tomd()
convert = _inst.convert


这段代码是一个用于将HTML转换为Markdown的工具类。它使用了正则表达式来解析HTML标签,并根据预定义的转换规则将其转换为对应的Markdown格式。


代码中定义了一个`Element`类,用于表示HTML中的各个元素。`Element`类包含了标签的起始位置、结束位置、内容、标签类型等信息。它还提供了一个`parse_inline`方法,用于解析内联元素,并将其转换为Markdown格式。


`Tomd`类是主要的转换类,它接受HTML字符串并提供了`convert`方法来执行转换操作。`convert`方法遍历预定义的HTML标签模式,并使用正则表达式匹配HTML字符串中对应的部分。然后创建相应的`Element`对象并进行转换操作。最后,将转换后的Markdown字符串返回。


在模块顶部,`MARKDOWN`字典定义了各个HTML标签对应的Markdown格式。`BlOCK_ELEMENTS`和`INLINE_ELEMENTS`字典定义了正则表达式模式,用于匹配HTML字符串中的块级元素和内联元素。`DELETE_ELEMENTS`列表定义了需要删除的HTML元素。



> 
> 那么既然有了转markdown的工具,我们就可以对网页进行转换
> 
> 
> 


## 进行转换



> 
> 首先,`result_file`函数用于创建一个保存结果文件的路径。它接受文件夹的用户名、文件名和文件夹名作为参数,并在指定的文件夹路径下创建一个新的文件,并返回该文件的路径。
> 
> 
> `get_headers`函数用于从一个文本文件中读取Cookie,并将它们保存为字典形式。它接受包含Cookie的文本文件路径作为参数。
> 
> 
> `delete_ele`函数用于删除`BeautifulSoup`对象中指定的标签。它接受一个BeautifulSoup对象和待删除的标签列表作为参数,并通过使用该对象的select方法来选择要删除的标签,然后使用`decompose`方法进行删除。
> 
> 
> `delete_ele_attr`函数用于删除BeautifulSoup对象中指定标签的指定属性。它接受一个BeautifulSoup对象和待删除的属性列表作为参数,并使用`find_all`方法来选取所有标签,然后使用Python的del语句删除指定的属性。
> 
> 
> `delete_blank_ele`函数用于删除BeautifulSoup对象中的空白标签。它接受一个BeautifulSoup对象和一个例外列表,对于不在例外列表中且内容为空的标签,使用decompose方法进行删除。
> 
> 
> `TaskQueue`类是一个简单的任务队列,用于存储已访问的和未访问的URL。它提供了一系列方法来操作这些列表。
> 
> 
> 



def result_file(folder_username, file_name, folder_name):
folder = os.path.join(os.path.dirname(os.path.realpath(file)), “…”, folder_name, folder_username)
if not os.path.exists(folder):
try:
os.makedirs(folder)
except Exception:
pass
path = os.path.join(folder, file_name)
file = open(path,“w”)
file.close()
else:
path = os.path.join(folder, file_name)
return path

def get_headers(cookie_path:str):
cookies = {}
with open(cookie_path, “r”, encoding=“utf-8”) as f:
cookie_list = f.readlines()
for line in cookie_list:
cookie = line.split(“:”)
cookies[cookie[0]] = str(cookie[1]).strip()
return cookies

def delete_ele(soup:BeautifulSoup, tags:list):
for ele in tags:
for useless_tag in soup.select(ele):
useless_tag.decompose()

def delete_ele_attr(soup:BeautifulSoup, attrs:list):
for attr in attrs:
for useless_attr in soup.find_all():
del useless_attr[attr]

def delete_blank_ele(soup:BeautifulSoup, eles_except:list):
for useless_attr in soup.find_all():
try:
if useless_attr.name not in eles_except and useless_attr.text == “”:
useless_attr.decompose()
except Exception:
pass

class TaskQueue(object):
def __init__(self):
self.VisitedList = []
self.UnVisitedList = []

def getVisitedList(self):
	return self.VisitedList

def getUnVisitedList(self):
	return self.UnVisitedList

def InsertVisitedList(self, url):
	if url not in self.VisitedList:
		self.VisitedList.append(url)

def InsertUnVisitedList(self, url):
	if url not in self.UnVisitedList:
		self.UnVisitedList.append(url)

def RemoveVisitedList(self, url):
	self.VisitedList.remove(url)

def PopUnVisitedList(self,index=0):
	url = []
	if index and self.UnVisitedList:
		url = self.UnVisitedList[index]
		del self.UnVisitedList[:index]
	elif self.UnVisitedList:
		url = self.UnVisitedList.pop()
	return url

def getUnVisitedListLength(self):
	return len(self.UnVisitedList)

class CSDN(object):
def __init__(self, username, folder_name, cookie_path):
# self.headers = {
# “User-Agent”: “Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.70 Safari/537.36”
# }
self.headers = get_headers(cookie_path)
self.s = requests.Session()
self.username = username
self.TaskQueue = TaskQueue()
self.folder_name = folder_name
self.url_num = 1

def start(self):
	num = 0
	articles = [None]
	while len(articles) > 0:
		num += 1
		url = u'https://blog.csdn.net/' + self.username + '/article/list/' + str(num)
		response = self.s.get(url=url, headers=self.headers)
		html = response.text
		soup = BeautifulSoup(html, "html.parser")
		articles = soup.find_all('div', attrs={"class":"article-item-box csdn-tracking-statistics"})
		for article in articles:
			article_title = article.a.text.strip().replace(' ',':')
			article_href = article.a['href']
			with ensure_memory(sys.getsizeof(self.TaskQueue.UnVisitedList)):
				self.TaskQueue.InsertUnVisitedList([article_title, article_href])

def get\_md(self, url):
	response = self.s.get(url=url, headers=self.headers)
	html = response.text
	soup = BeautifulSoup(html, 'lxml')
	content = soup.select_one("#content\_views")
	# 删除注释
	for useless_tag in content(text=lambda text: isinstance(text, Comment)):
		useless_tag.extract()
	# 删除无用标签
	tags = ["svg", "ul", ".hljs-button.signin"]
	delete_ele(content, tags)
	# 删除标签属性
	attrs = ["class", "name", "id", "onclick", "style", "data-token", "rel"]
	delete_ele_attr(content,attrs)
	# 删除空白标签
	eles_except = ["img", "br", "hr"]
	delete_blank_ele(content, eles_except)
	# 转换为markdown
	md = Tomd(str(content)).markdown
	return md


def write\_readme(self):
	print("+"\*100)
	print("[++] 开始爬取 {} 的博文 ......".format(self.username))
	print("+"\*100)
	reademe_path = result_file(self.username,file_name="README.md",folder_name=self.folder_name)
	with open(reademe_path,'w', encoding='utf-8') as reademe_file:
		readme_head = "# " + self.username + " 的博文\n"
		reademe_file.write(readme_head)
		for [article_title,article_href] in self.TaskQueue.UnVisitedList[::-1]:
				text = str(self.url_num) + '. [' + article_title + ']('+ article_href +')\n'
				reademe_file.write(text)
				self.url_num += 1
	self.url_num = 1

def get\_all\_articles(self):
	try:
		while True:
			[article_title,article_href] = self.TaskQueue.PopUnVisitedList()
			try:
				file_name = re.sub(r'[\/::\*?"<>|]','-', article_title) + ".md"
				artical_path = result_file(folder_username=self.username, file_name=file_name, folder_name=self.folder_name)
				md_head = "# " + article_title + "\n"
				md = md_head + self.get_md(article_href)
				print("[++++] 正在处理URL:{}".format(article_href))
				with open(artical_path, "w", encoding="utf-8") as artical_file:
					artical_file.write(md)
			except Exception:
				print("[----] 处理URL异常:{}".format(article_href))
			self.url_num += 1
	except Exception:
		pass

def muti\_spider(self, thread_num):
	while self.TaskQueue.getUnVisitedListLength() > 0:
		thread_list = []
		for i in range(thread_num):
			th = threading.Thread(target=self.get_all_articles)
			thread_list.append(th)
		for th in thread_list:
			th.start()

lock = threading.Lock()
total_mem= 1024 * 1024 * 500 #500MB spare memory
@contextlib.contextmanager
def ensure_memory(size):
global total_mem
while 1:
with lock:
if total_mem > size:
total_mem-= size
break
time.sleep(5)
yield
with lock:
total_mem += size

def spider_user(username: str, cookie_path:str, thread_num: int = 10, folder_name: str = “articles”):
if not os.path.exists(folder_name):
os.makedirs(folder_name)
csdn = CSDN(username, folder_name, cookie_path)
csdn.start()
th1 = threading.Thread(target=csdn.write_readme)
th1.start()
th2 = threading.Thread(target=csdn.muti_spider, args=(thread_num,))
th2.start()

def spider(usernames: list, cookie_path:str, thread_num: int = 10, folder_name: str = “articles”):
for username in usernames:
try:
user_thread = threading.Thread(target=spider_user,args=(username, cookie_path, thread_num, folder_name))
user_thread.start()
print(“[++] 开启爬取 {} 博文进程成功 …”.format(username))
except Exception:
print(“[–] 开启爬取 {} 博文进程出现异常 …”.format(username))


文末有福利领取哦~
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

👉**一、Python所有方向的学习路线**

Python所有方向的技术点做的整理,形成各个领域的知识点汇总,它的用处就在于,你可以按照上面的知识点去找对应的学习资源,保证自己学得较为全面。![img](https://img-blog.csdnimg.cn/c67c0f87cf9343879a1278dfb067f802.png)

👉**二、Python必备开发工具**

![img](https://img-blog.csdnimg.cn/757ca3f717df4825b7d90a11cad93bc7.png)  
👉**三、Python视频合集**

观看零基础学习视频,看视频学习是最快捷也是最有效果的方式,跟着视频中老师的思路,从基础到深入,还是很容易入门的。  
![img](https://img-blog.csdnimg.cn/31066dd7f1d245159f21623d9efafa68.png)

👉 **四、实战案例**

光学理论是没用的,要学会跟着一起敲,要动手实操,才能将自己的所学运用到实际当中去,这时候可以搞点实战案例来学习。**(文末领读者福利)**  
![img](https://img-blog.csdnimg.cn/e78afb3dcb8e4da3bae5b6ffb9c07ec7.png)

👉**五、Python练习题**

检查学习结果。  
![img](https://img-blog.csdnimg.cn/280da06969e54cf180f4904270636b8e.png)

👉**六、面试资料**

我们学习Python必然是为了找到高薪的工作,下面这些面试题是来自阿里、腾讯、字节等一线互联网大厂最新的面试资料,并且有阿里大佬给出了权威的解答,刷完这一套面试资料相信大家都能找到满意的工作。  
![img](https://img-blog.csdnimg.cn/a9d7c35e6919437a988883d84dcc5e58.png)

![img](https://img-blog.csdnimg.cn/5db8141418d544d3a8e9da4805b1a3f9.png)

👉因篇幅有限,仅展示部分资料,这份完整版的Python全套学习资料已经上传




**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

**[需要这份系统化学习资料的朋友,可以戳这里无偿获取](https://bbs.csdn.net/topics/618317507)**

**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**
  • 22
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值