c - 如何使用 C 抓取网页?

标签 c web-scraping

所以我使用 HTML Agility 包用 C# 编写了一个网站抓取程序。这是相当简单的。即使考虑到网页格式的不一致,我仍然只花了几个小时就开始工作。

现在,我必须用 C 重新实现这个程序,以便它可以在 linux 环境中运行。这是一场重大的噩梦。

我能够拉回页面,但是当涉及到跟踪它以拉出我感兴趣的部分时 - 我画了很多空白。最初,除了使用 Tidy 和其他一些 XML 库之外,我一直试图在 C# 中实现类似于我的 HTML Agility 选项的解决方案,这样我可以或多或少地保持我的逻辑相同。

这不是很好。我有权访问的 XML 库似乎不支持 xpath,而且我无法安装支持的库。所以我求助于尝试找出一种方法来使用字符串匹配来阅读页面以找到我想要的数据。我不禁觉得必须有更好的方法来做到这一点。

这是我的:

#define HTML_PAGE "codes.html"

int extract()
{

    FILE *html;

    int found = 0;
    char buffer[1000];
    char searchFor[80], *cp;

    html = fopen(HTML_PAGE, "r");

    if (html)
    {

        // this is too error prone, if the buffer cuts off half way through a section of the string we are looking for, it will fail!
        while(fgets(buffer, 999, html))
        {
            trim(buffer);

            if (!found)
            {
                sprintf(searchFor, "<strong>");
                cp = (char *)strstr(buffer, searchFor);
                if(!cp)continue;

                if (strncmp(cp + strlen(searchFor), "CO1", 3) == 0 || strncmp(cp + strlen(searchFor), "CO2", 3) == 0)
                {
                    got_code(cp + strlen(searchFor));
                }
            }
        }
    }

    fclose(html);

    return 0;
}

got_code(html)
    char    *html;
{
    char    code[8];
    char    *endTag;
    struct  _code_st    *currCode;
    int i;  

    endTag = (char *)strstr(html, "</strong>");
    if(!endTag)return;

    sprintf(code, "%.7s", html);

    for(i=0 ; i<Data.Codes ; i++)
        if(strcasecmp(Data.Code[i].Code, code)==0)
           return;

    ADD_TO_LIST(currCode, _code_st, Data.Code, Data.Codes);
    currCode->Code = (char *)strdup(code);

    printf("Code: %s\n", code);
}

以上不能正常工作。我得到了很多我感兴趣的代码,但正如我上面提到的,如果缓冲区在错误的位置切断,我会错过一些。

我确实尝试过将我感兴趣的整个 html block 读入一个字符串,但我无法弄清楚如何循环它 - 我无法显示任何代码。

有谁知道我该如何解决这个问题?

编辑:我一直在考虑这个问题。有什么方法可以让我在文件中向前看并搜索我正在解析的每个文本“ block ”的末尾并将缓冲区大小设置为读取之前的大小?我需要另一个指向同一个文件的文件指针吗?这将(希望)防止缓冲区在不方便的地方切断的问题。

最佳答案

好吧,在多次用头撞墙试图想出一种方法让我的上述代码工作之后,我决定尝试一种稍微不同的方法。

因为我知道我正在抓取的页面上的数据包含在一个大行中,所以我更改了我的代码以搜索文件直到找到它。然后我继续寻找我想要的积木。这工作得非常好,一旦我让代码读取了一些 block ,就很容易进行小的修改以解决 HTML 中的不一致问题。花时间最长的部分是弄清楚一旦我到达行尾如何跳出,我通过提前达到峰值以确保还有另一个 block 要读取来解决这个问题。

这是我的代码(丑陋但实用):

#define HTML_PAGE "codes.html"
#define START_BLOCK "<strong>"
#define END_BLOCK "</strong>"

int extract()
{

    FILE *html;

    int found = 0;
    char *line = NULL, *endTag, *startTag;
    size_t len = 0;
    ssize_t read;

    char searchFor[80];

    html = fopen(HTML_PAGE, "r");

    if (html)
    {
        while((read = getline(&line, &len, html)) != -1)
        {
            if (found) // found line with codes we are interested in
            {
                char   *ptr = line;
                size_t nlen = strlen (END_BLOCK);

                while (ptr != NULL) 
                {
                    sprintf(searchFor, START_BLOCK);
                    startTag = (char *)strstr(ptr, searchFor);
                    if(!startTag)
                    {
                        nlen = strlen (START_BLOCK);
                        ptr += nlen;
                        continue;
                    }

                    if (strncmp(startTag + strlen(searchFor), "CO1", 3) == 0 || strncmp(startTag + strlen(searchFor), "CO2", 3) == 0)
                        got_code(startTag + strlen(searchFor), code);
                    else {
                        nlen = strlen (START_BLOCK);
                        ptr += nlen;
                        continue;
                    }

                    sprintf(searchFor, END_BLOCK);
                    ptr = (char *)strstr(ptr, searchFor);

                    if (!ptr) { found = 0; break; }

                    nlen = strlen (END_BLOCK);                  
                    ptr += nlen;

                    if (ptr)
                    {
                        // look ahead to make sure we have more to pull out
                        sprintf(searchFor, END_BLOCK);
                        endTag = (char *)strstr(ptr, searchFor);
                        if (!endTag) { break; }
                    }
                }

                found = 0;
                break;
            }

            // find the section of the downloaded page we care about
            // the next line we read will be a blob containing the html we want
            if (strstr(line, "wiki-content") != NULL)
            {
                found = 1;
            }
        }

        fclose(html);
    }

    return 0;
}

got_code(char *html)
{
    char    code[8];
    char    *endTag;
    struct  _code_st    *currCode;
    int i;  

    endTag = (char *)strstr(html, "</strong>");
    if(!endTag)return;

    sprintf(code, "%.7s", html);

    for(i=0 ; i<Data.Codes ; i++)
        if(strcasecmp(Data.Code[i].Code, code)==0)
            return;

    ADD_TO_LIST(currCode, _code_st, Data.Code, Data.Codes);
    currCode->Code = (char *)strdup(code);

    printf("Code: %s\n", code);
}

虽然不如我的 C# 程序优雅或健壮,但至少它可以提取我想要的所有信息。

关于c - 如何使用 C 抓取网页?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26416673/

相关文章:

c - 等待来自文件描述符的输入

java - 如何从网页中提取文本内容?

c - 欧拉计划 #179

c - numa_alloc_onnode() 分配的内存多于它所能分配的内存吗?

javascript - 网页抓取,我无法选择我想要的标签

python - 使用 FOR 循环时如何在 item.add_css() 中编写正确的 CSS?

python - 如何在 Python 中更快、更高效地抓取多个页面

ios - 网页抓取 Facebook 的最佳方式是什么

对更新 C 中的结构成员感到困惑

比较整个数组行中的一组变量