【问题标题】:Scrape and parse the courses using requests and BeautifulSoup使用 requests 和 BeautifulSoup 抓取和解析课程
【发布时间】:2021-11-02 00:18:49
【问题描述】:

我正在制定下学期的时间表。我想提高效率,所以我试图抓取我大学的页面以提取所有课程及其先决条件。我将 Python 与 requestsBeautifulSoup 一起使用。

我在提取必须的先决条件和推荐的先决条件列表时遇到了一些麻烦。示例:

<div id="content">
    <!-- ... -->
    <p>
        <img src="gifs/triangle.jpg" width="5" height="10" alt="" border="0" /> Must: The courses
        <a href="DOMAIN/courses/12345.htm" target="_blank">Linear Algebra 101</a>,
        <a href="DOMAIN/courses/12346.htm" target="_blank">Calculus 101</a> (‏12346‎)‏<span class="heara"><a href="#remarks">2</a></span> and one of 
        <a href="DOMAIN/courses/12347.htm" target="_blank">Introduction to Statistics and Probability of Science</a>, <a href="DOMAIN/courses/11231.htm" target="_blank">Probability and Introduction to Computer Science Statistics</a>,
        <a href="DOMAIN/courses/12348.htm" target="_blank">Probability theory</a>. Recommended: <a href="DOMAIN/courses/12349‎.htm" target="_blank">Calculus 102</a> (‏12349‎)‏.
        <span class="heara"><a href="#remarks">3 </a></span>
    </p>
    <!-- ... -->
</div>

在这种情况下,内容是:

必修课程:线性代数 101、微积分 101 (‏12346‎)‏2 和统计学概论和概率科学、概率和计算机科学统计学概论、概率论之一。推荐:微积分 102 (‏12349‎)‏。 3

一些注意事项:

  • 在这种情况下Linear Algebra 101Calculus 101 是必须的。也是Introduction to Statistics and Probability of Science, Probability and Introduction to Computer Science Statistics, Probability theory 之一。
  • 推荐:Calculus 102
  • 请注意,有些课程带有编号,有些则没有(例如Calculus 101)。
  • 请注意,有一个 #remarks(在我们的例子中是粗体数字 2)。

我正在尝试创建一个字典,例如:

{
  "must_courses": [
    "12345",
    "12346"
  ],
  "must_one_of": [
    [
      "12347",
      "11231",
      "12348"
    ]
  ],
  "recommended": [
    "12349"
  ]
}

但由于某种原因,它真的很难解析,因为:

  • 首先,“内容”div 中有几个&lt;p&gt;,由于&lt;p&gt; 没有class/id,所以很难找到合适的&lt;p&gt;&lt;p&gt; 号码不固定。通常是第二个或第三个&lt;p&gt;
  • 某些课程名称的名称中没有课程编号,因此应从&lt;a href=$url 中提取课程编号。
  • 我需要检查它的格式是否正确 - 它可以以Must 开头,也可以以Recommended 开头。它也可以有“and one of”课程。

我尝试了什么:

soup = bs(r.content, 'html.parser')
content_div = soup.find('div', attrs={'id':'content'})
p_sections = content_div.find_all('p')
for index, p_section in enumerate(p_sections):
    if index == 3:
        # Sometimes its index 2 and sometimes its index 3
        # Tried to use: p_section.text

即使知道&lt;p&gt;是正确的哪个索引,我仍然不知道如何解析该行。

几个例子: 示例 1:

<div id="content">
    <!-- ... -->
    <p>
        <img src="gifs/triangle.jpg" width="5" height="10" alt="" border="0" /> Must: The course <a href="DOMAIN/courses/12346.htm" target="_blank">Calculus 101</a> (‏12346‎)‏.
        <span class="heara"><a href="#remarks">2</a></span>
    </p>
    <!-- ... -->
</div>

内容:

必须:微积分 101 (‏12346‎)‏ 课程。 2

示例 2:

<div id="content">
    <!-- ... -->
    <p><img src="gifs/triangle.jpg" width="5" height="10" alt="" border="0" />
 Recommended: The course <a href="DOMAIN/courses/04101.htm" target="_blank">Combinatorics</a>.</p>
    <!-- ... -->
</div>

内容:

推荐:课程组合。

但我只需要一个关于如何“正确”解析它的示例,我认为我可以使用正则表达式(我认为这是这样做的方法?)来解析不同的行。添加示例以便更容易理解我面临的任务。

【问题讨论】:

    标签: python html beautifulsoup python-requests


    【解决方案1】:

    这是我为第一个示例所做的。找到您要抓取的内容:

    html = """\
    <div id="content">
        <!-- ... -->
        <p>
            <img src="gifs/triangle.jpg" width="5" height="10" alt="" border="0" /> Must: The courses
            <a href="DOMAIN/courses/12345.htm" target="_blank">Linear Algebra 101</a>,
            <a href="DOMAIN/courses/12346.htm" target="_blank">Calculus 101</a> (‏12346‎)‏<span class="heara"><a href="#remarks">2</a></span> and one of 
            <a href="DOMAIN/courses/12347.htm" target="_blank">Introduction to Statistics and Probability of Science</a>, <a href="DOMAIN/courses/11231.htm" target="_blank">Probability and Introduction to Computer Science Statistics</a>,
            <a href="DOMAIN/courses/12348.htm" target="_blank">Probability theory</a>. Recommended: <a href="DOMAIN/courses/12349‎.htm" target="_blank">Calculus 102</a> (‏12349‎)‏.
            <span class="heara"><a href="#remarks">3 </a></span>
        </p>
        <!-- ... -->
    </div>
    """
    
    soup = BeautifulSoup(html, 'lxml')
    content = soup.find(attrs={'id': 'content'})
    

    用类名heara提取不需要的span标签:

    [unwanted.extract() for unwanted in content.find_all('span', class_="heara")]
    

    从 href 标记中获取所有 url 以形成 id:

    raw_urls = [each['href'] for each in content.find_all('a')]
    ids = [re.findall(r'\d+', url)[0] for url in raw_urls]
    # -> ['12345', '12346', '12347', '11231', '12348', '12349']
    

    抓取a标签内的所有文本和内容本身。按关键字拆分内容文本,例如and one ofRecommended,以与a标签内的单词进行比较:

    raw_text = [each.text for each in content.find_all('a')]
    
    # grab texts by their importance
    raw_must_take = content.text.strip().split('and one of')[0]
    raw_must_one_of = content.text.strip().split('and one of')[1].split('Recommended')[0]
    raw_recommended = content.text.strip().split('Recommended')[1]
    

    将标签中的每个单词与对应的字符串进行比较,形成干净的课程:

    clean_must_take = [each for each in raw_text if each in raw_must_take]
    clean_must_one_of = [each for each in raw_text if each in raw_must_one_of]
    clean_recommended = [each for each in raw_text if each in raw_recommended]
    """
    ['Linear Algebra 101', 'Calculus 101']
    ['Introduction to Statistics and Probability of Science', 'Probability and Introduction to Computer Science Statistics', 'Probability theory']
    ['Calculus 102']
    """
    

    根据每个干净列表的长度查找ids范围,形成字典:

    must_take_range = [0, len(clean_must_take)]
    must_one_of_range = [len(clean_must_take), len(clean_must_take) + len(clean_must_one_of)]
    recommended_range = [len(clean_must_take) + len(clean_must_one_of), len(ids)]
    
    
    d = defaultdict(list)
    [d["must_take"].append(ids[i]) for i in range(*must_take_range)]
    [d["must_one_of"].append(ids[i]) for i in range(*must_one_of_range)]
    [d["recommended"].append(ids[i]) for i in range(*recommended_range)]
    

    最终结果如下所示:

    defaultdict(<class 'list'>, {'must_take': ['12345', '12346'], 'must_one_of': ['12347', '11231', '12348'], 'recommended': ['12349']})
    

    进口:

    import re
    from bs4 import BeautifulSoup
    from collections import defaultdict
    

    PS:我知道这不是最好的解决方案,但它确实有效:)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-04-22
      • 1970-01-01
      • 2019-10-29
      • 2022-01-11
      • 2015-04-20
      • 2020-02-06
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多